如何结合使用DataTrigger和EventTrigger?

18

注意:我已经问了相关问题(有一个被接受的答案):如何结合DataTrigger和Trigger?

我认为我需要结合一个EventTrigger和一个DataTrigger来实现我想要的效果:

  • 当我的ListBox中出现一个项目时,它应该闪烁几秒钟
  • 如果该项是“Critical”,则应保持突出显示

目前我拥有一个如下所示的DataTemplate:

<DataTemplate DataType="{x:Type Notifications:NotificationViewModel}">
    <Grid HorizontalAlignment="Stretch">
        <Border Name="Background" CornerRadius="8" Background="#80c0c0c0" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" />
        <Border Name="Highlight"  CornerRadius="8" Background="Red"       HorizontalAlignment="Stretch" VerticalAlignment="Stretch" />
        <!-- snip actual visual stuff -->
        <Grid.Triggers>
            <EventTrigger RoutedEvent="Grid.Loaded">
                <EventTrigger.Actions>
                    <BeginStoryboard>
                        <Storyboard>
                            <DoubleAnimation x:Name="LoadedAnimation" 
                                             Storyboard.TargetName="Highlight" 
                                             Storyboard.TargetProperty="Opacity" 
                                             From="0" To="1" 
                                             RepeatBehavior="5x" 
                                             Duration="0:00:0.2" 
                                             AutoReverse="True" />
                        </Storyboard>
                    </BeginStoryboard>
                </EventTrigger.Actions>
            </EventTrigger>
        </Grid.Triggers>
    </Grid>
    <DataTemplate.Triggers>
        <DataTrigger Binding="{Binding Path=IsCritical}" Value="True">
            <Setter TargetName="LoadedAnimation" Property="RepeatBehavior" Value="5.5x" />
        </DataTrigger>
    </DataTemplate.Triggers>
</DataTemplate>
这个想法是使用EventTrigger来使Highlight边框的不透明度在0和1之间重复动画,从而在项目首次加载时吸引用户的注意力。 DataTrigger确定要执行动画的次数。如果视图模型报告该项目IsCritical,则会执行5.5次动画(以不透明度1结束),否则执行5次动画(以不透明度0结束)。

但是上面的XAML无法工作,因为DataTrigger的setter出现以下错误:

未在VisualTree中找到名称为'LoadedAnimation'的子级。

说得对。那么,除了使用自定义值转换器或将动画计数放在视图模型上并绑定到它之外,我还有哪些选择?


这可能会有帮助:https://dev59.com/rHE85IYBdhLWcg3wdDSz - Lonli-Lokli
4个回答

1
我知道你说你不太喜欢转换器的想法,但是看起来 Blend 解决方案需要安装一个库。转换器并不需要太多工作量,并且表明汇率直接取决于 IsCritical 属性的意图。
public class CriticalAnimationRateConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        // Error handling omitted for brevity.
        if ((bool)value)
            return new System.Windows.Media.Animation.RepeatBehavior(5.5);
        else
            return new System.Windows.Media.Animation.RepeatBehavior(5.0);
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

然后更新您的动画:
<DoubleAnimation Storyboard.TargetName="Highlight"
                 Storyboard.TargetProperty="Opacity"
                 From="0"
                 To="1"
                 RepeatBehavior="{Binding IsCritical, Converter={StaticResource CriticalAnimationRateConverter}}"
                 Duration="0:00:0.2"
                 AutoReverse="True" />

然后可以删除 DataTrigger

0
如果您可以访问Blend SDK(如果您使用的是VS2012+,则应该可以),则应该能够完全在XAML中完成此操作,类似于以下内容(免责声明:未经测试):
<Grid HorizontalAlignment="Stretch">
    <VisualStateManager.VisualStateGroups>
        <VisualStateGroup x:Name="NotificationStates">
            <VisualState x:Name="Flashing">
                <Storyboard>
                    <DoubleAnimation x:Name="LoadedAnimation" 
                                     Storyboard.TargetName="Highlight" 
                                     Storyboard.TargetProperty="Opacity" 
                                     From="0" To="1" 
                                     RepeatBehavior="5x" 
                                     Duration="0:00:0.2" 
                                     AutoReverse="True" />
                </Storyboard>
            </VisualState>
            <VisualState x:Name="Normal" />
        </VisualStateGroup>
    </VisualStateManager.VisualStateGroups>
    <Border Name="Background" CornerRadius="8" Background="#80c0c0c0" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" />
    <Border Name="Highlight"  CornerRadius="8" Background="Red"       HorizontalAlignment="Stretch" VerticalAlignment="Stretch" />
    <!-- snip actual visual stuff -->
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="Loaded">
            <ic:GoToStateAction StateName="Flashing"/>
        </i:EventTrigger>
        <ie:DataTrigger Binding="{Binding Path=IsCritical}" Value="True">
            <ic:GoToStateAction StateName="Flashing"/>
        </ie:DataTrigger>
    </i:Interaction.Triggers>
</Grid>

将你的Storyboard提取到VisualState中,然后使用表达式库在XAML中切换状态。你需要Microsoft.Expression.Interactions库,请参见WPF/Silverlight States - Activate from XAML?


0
在这种情况下,我会使用行为而不是触发器。您可以编写一个行为,将事件处理程序附加到相关对象的加载事件上,然后应用动画。该行为可能会公开一些属性,我会公开一个AnimationCount(int)属性,告诉行为如何在与其关联的元素上重复多少次动画。然后,您可以将此属性绑定到视图模型中的IsCritical属性,并使用值转换器将false转换为5,将true转换为5.5。
希望这有所帮助。

-4
尝试像这样做:

<Style x:Key="EventTriggerStyleKey">
  <Style.Triggers>
    <EventTrigger RoutedEvent="some event here">
      <!-- your animation here -->
    </EventTrigger>
  <Style.Triggers>
</Style>

<Style x:Key="myStyleKey">
  <Style.Triggers>
    <DataTrigger Binding="....." Value="......">
      <Setter Property="........." Value="......."/>
      <Setter Property="Style" Value="{StaticResource EventTriggerStyleKey}"/>
    </DataTrigger>
  <Style.Triggers>
</Style>

6
你试过这个吗?我的经验是,你不能拥有一个设定风格的风格。 - Drew Noakes
1
你不能在样式内部设置另一个样式。 - Joel Barsotti

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接