在WPF中的Window.Resources中声明时,EventTrigger不起作用

时间:2013-07-23 04:11:18

标签: wpf events triggers styles

我是WPF的新手,所以我可能会遗漏一些必要的东西,但我已经尝试并尝试对以下现象提出解释,但无济于事。 基本上,以下代码可以工作(显示动画):

    <Window.Resources>
    <Storyboard x:Key="LoadStoryBoard"
AutoReverse="True"
RepeatBehavior="Forever">
        <DoubleAnimationUsingKeyFrames Storyboard.TargetName="button1" 
                Storyboard.TargetProperty="(Button.Opacity)">
            <EasingDoubleKeyFrame KeyTime="0:0:0.7" Value="0.4" />
        </DoubleAnimationUsingKeyFrames>
    </Storyboard>
</Window.Resources>
...
<Button x:Name="button1" Grid.Column="0" Grid.Row="1" Style="{StaticResource Load}">
<Button.Triggers>
    <EventTrigger RoutedEvent="Loaded">
                <BeginStoryboard Storyboard="{StaticResource LoadStoryBoard}" />
            </EventTrigger>
</Button.Triggers>
</Button>

但是,当我尝试将偶数触发器置于下面的加载样式中时,动画将不再显示:

    <Window.Resources>
    <Storyboard x:Key="LoadStoryBoard"
AutoReverse="True"
RepeatBehavior="Forever">
        <DoubleAnimationUsingKeyFrames Storyboard.TargetName="button1" 
                Storyboard.TargetProperty="(Button.Opacity)">
            <EasingDoubleKeyFrame KeyTime="0:0:0.7" Value="0.4" />
        </DoubleAnimationUsingKeyFrames>
    </Storyboard>
</Window.Resources>
...
<Style x:Key="Load" TargetType="Button">
...
<Style.Triggers>
    ...
    <EventTrigger RoutedEvent="Loaded">
                <BeginStoryboard Storyboard="{StaticResource LoadStoryBoard}" />
            </EventTrigger>
</Style.Triggers>
</Style>

2 个答案:

答案 0 :(得分:3)

Style触发器中无法使用TargetName这样的动画对象。为此,将它们放在触发器模板<ControlTemplate.Triggers>中。引自link

  

TargetName不适用于Style的Triggers集合。样式没有名称范围,因此在那里按名称引用元素没有意义。但是模板(DataTemplate或ControlTemplate)确实有一个名称范围。

以下作品:

<Window.Resources>
    <Storyboard x:Key="LoadStoryBoard" AutoReverse="True" RepeatBehavior="Forever">
        <DoubleAnimationUsingKeyFrames Storyboard.TargetName="button1" Storyboard.TargetProperty="(Button.Opacity)">
            <EasingDoubleKeyFrame KeyTime="0:0:0.7" Value="0.4" />
        </DoubleAnimationUsingKeyFrames>
    </Storyboard>       

    <Style x:Key="ButtonStyle" TargetType="{x:Type Button}">
        <Setter Property="Background" Value="Green" />
        <Setter Property="Foreground" Value="White" />
        <Setter Property="FontSize" Value="14" />
        <Setter Property="FocusVisualStyle" Value="{x:Null}" />
        <Setter Property="SnapsToDevicePixels" Value="True" />

        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type Button}">
                    <Border x:Name="button1" CornerRadius="0" Background="{TemplateBinding Background}">
                        <Grid>
                            <ContentPresenter x:Name="MyContentPresenter" Content="{TemplateBinding Content}" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="0,0,0,0" />                                
                        </Grid>
                    </Border>

                    <ControlTemplate.Triggers>
                        <Trigger Property="IsMouseOver" Value="True">
                            <Setter Property="Background" Value="Orange" />
                        </Trigger>

                        <EventTrigger RoutedEvent="Button.Loaded">
                            <BeginStoryboard Storyboard="{StaticResource LoadStoryBoard}" />
                        </EventTrigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</Window.Resources>

<Grid>
    <Button Name="TestButton" Style="{StaticResource ButtonStyle}" Width="100" Height="30" Content="Test" Grid.Column="0" Grid.Row="1" />
</Grid>

请注意TargetName中指定的模板中现在Border<Border x:Name="button1" .../>

Note: 或者,您可以删除Storyboard.TargetName,因为它不支持触发样式。

答案 1 :(得分:3)

你是正确的EventTrigger不起作用,但不是因为它是在Resources部分声明的。要看到这一点,您可以将您的样式直接移到Button声明中,但它仍然不起作用:

<Button x:Name="button1" Grid.Column="0" Grid.Row="1">
    <Button.Style>
        <Style TargetType="Button">
            <Style.Triggers>
                <EventTrigger RoutedEvent="Button.Loaded">
                    <BeginStoryboard Storyboard="{StaticResource LoadStoryBoard}" />
                </EventTrigger>
            </Style.Triggers>
        </Style>
    </Button.Style>
</Button>

但是,如果我们从Animation部分移动Resources的声明,它会再次有效:

<Button x:Name="button1" Grid.Column="0" Grid.Row="1">
    <Button.Style>
        <Style TargetType="Button">
            <Style.Triggers>
                <EventTrigger RoutedEvent="Button.Loaded">
                    <BeginStoryboard>
                        <Storyboard AutoReverse="True" RepeatBehavior="Forever">
                            <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(Button.Opacity)">
                                <EasingDoubleKeyFrame KeyTime="0:0:0.7" Value="0.4" />
                            </DoubleAnimationUsingKeyFrames>
                        </Storyboard>
                    </BeginStoryboard>
                </EventTrigger>
            </Style.Triggers>
        </Style>
    </Button.Style>
</Button>

因此问题似乎与Storyboard部分中声明的ResourcesLoaded事件触发时尚未准备就绪有关。 this post中也提到了类似的问题。

但是,如果我们将Animation的完整声明放入Style部分中声明的Resources,那么只是为了让事情更加混乱,那么现在Style工作原理:

<Window.Resources>
    <Style x:Key="Load" TargetType="Button">
        <Style.Triggers>
            <EventTrigger RoutedEvent="Button.Loaded">
                <BeginStoryboard>
                    <Storyboard AutoReverse="True" RepeatBehavior="Forever">
                        <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(Button.Opacity)">
                            <EasingDoubleKeyFrame KeyTime="0:0:0.7" Value="0.4" />
                        </DoubleAnimationUsingKeyFrames>
                    </Storyboard>
                </BeginStoryboard>
            </EventTrigger>
        </Style.Triggers>
    </Style>
</Window.Resources>
<Button x:Name="button1" Grid.Column="0" Grid.Row="1" Style="{StaticResource Load}" />

我可以推测为什么会发生这种情况,但我猜测很少有WPF开发人员真正知道为什么一切都是这样......我已经了解到如果某个特定的声明方法有效,使用它,如果没有,尝试另一个。

<强>背景

在WPF中,有四个地方我们可以定义Triggers; Style.TriggersControlTemplate.TriggersDataTemplate.TriggersFrameworkElement.Triggers(例如Button.Triggers)。

基本上,FrameworkElement.Triggers TriggerCollection存在一个巨大缺陷,因为它只接受EventTrigger类型的触发器。这可以在MSDN的FrameworkElement.Triggers Property页面上看到,其中给出了以下关于此属性可以接受的内容的定义:

  

一个或多个已定义的EventTrigger元素。每个这样的触发器是   预计包含有效的故事板操作和参考。注意   此集合只能在页面的根元素上建立。

其他触发器属性的MSDN属性页每个都宣布他们可以接受零个或多个TriggerBase对象,或一个或多个TriggerBase对象

此外,有不同的规则,不同的触发器遵循 - 统一的方法肯定会帮助WPF的新手。来自FrameworkElement.Triggers Property页面:

  

此属性不允许您检查存在的触发器   此元素使用的部分样式。它只报告集合   在字面上添加到集合中的触发器   标记或代码。元素通常不存在这样的元素   默认情况下(例如通过模板);它更常见   来自控制合成的触发器   而不是样式。

     

在行为方面(并试图确定哪种效果来自   哪个元素声明了Triggers集合),都是触发器   条件和触发器效果可能在此元素上,或者可能是   在逻辑树中的子元素上。请注意,如果您使用   终身事件,例如Loaded来获得这个集合,孩子   元素的触发器可能尚未完全加载和集合   将比运行时真实的小。

     

请注意,仅在元素上建立的触发器集合   支持EventTrigger,而不是属性触发器(Trigger)。如果您需要   属性触发器,您必须将它们放在样式或模板中   然后直接将该样式或模板分配给元素   通过Style属性,或间接通过隐式样式   参考

来自MSDN的DataTemplate.Triggers Property页:

  

如果要在数据模板中创建触发器,则设置为   触发器应该是设置范围内的属性   数据模板。否则,它可能更适合创建   触发器使用以包含数据的类型为目标的样式。   例如,如果要绑定ListBox控件,则容器为   ListBoxItem对象。如果您使用触发器来设置属性   不在DataTemplate的范围内,那么它可能更多   适合创建ListBoxItem样式并在其中创建触发器   风格。

不幸的是,所有这些额外的信息实际上并没有回答你关于为什么动画资源在Style资源中不起作用的问题,但是现在希望你可以看到整个Trigger区域是一个复杂,凌乱的地方。我自己不是专家,我只是倾向于使用任何一种声明Trigger的方法。

我希望在某种程度上有所帮助。