6

我是 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>

但是,当我尝试将 eventtrigger 放在下面的 Load Style 中时,动画不再出现:

    <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>
4

2 回答 2

3

Style触发器中不能使用对象TargetName,这样的动画。为此,它们被放置在触发器模板中<ControlTemplate.Triggers>。引用链接

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>

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

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

于 2013-07-23T05:02:38.287 回答
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该部分中的声明Resources尚未准备好有关。这篇文章Loaded中提到了一个类似的问题。

但是,只是为了让事情更加混乱,如果我们然后将完整的声明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 中,我们可以定义四个地方TriggersStyle.Triggers, ControlTemplate.Triggers,DataTemplate.TriggersFrameworkElement.Triggers(例如Button.Triggers)。

基本上,它有一个巨大的缺陷FrameworkElement.Triggers TriggerCollection,它只接受 type 的触发器EventTrigger。这可以在 MSDN 的FrameworkElement.Triggers 属性页面上看到,其中对该属性可以接受的内容给出了以下定义:

一个或多个已定义的 EventTrigger 元素。每个此类触发器都应包含有效的故事板操作和引用。请注意,此集合只能在页面的根元素上建立。

其他触发器属性的 MSDN 属性页分别声明它们可以接受零个或多个 TriggerBase 对象,或者一个或多个 TriggerBase 对象

此外,不同的触发器遵循不同的规则——统一的方法肯定会帮助 WPF 的新手。从FrameworkElement.Triggers 属性页面:

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

在行为方面(并试图确定哪个效果来自哪个元素声明的 Triggers 集合),触发条件和触发效果都可能在此元素上,也可能在逻辑树中的子元素上。请注意,如果您使用诸如 Loaded 之类的生命周期事件来获取此集合,则子元素的触发器可能尚未完全加载,并且集合将小于运行时的实际值。

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

从MSDN的DataTemplate.Triggers 属性页面:

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

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

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

于 2013-07-23T09:56:38.090 回答