【问题标题】:What is the best way to start an animation when a bound value changes?当绑定值发生变化时,启动动画的最佳方式是什么?
【发布时间】:2010-11-10 11:11:51
【问题描述】:

这是经常出现的情况:

在视图中,您有一个绑定到 ViewModel 属性的控件(由 INotifyPropertyChanged 支持)。例如:

<TextBlock Text="{Binding Path=Subtotal}"/>

当属性发生变化时,你需要通过一些创意动画来引起用户的注意。我如何利用视图已经连接到通知的事实并避免创建大量额外代码(或至少创建一次并重复使用)。数据触发器可能是最好的选择,但我不知道如何让它们触发任何值更改而不是某个特定值。

想到以下选项:

  • 在 ViewModel 中引发附加事件,在 View 代码隐藏中订阅。
  • 使用转换器创建绑定到提到的属性的数据触发器,如果​​值发生变化,该转换器将返回 true。
  • 创建一个绑定到 ViewModel 上的新布尔属性的数据触发器,该属性用于“发出”更改的“信号”。
  • 创建附加到控件的行为,该行为将订阅控件的依赖属性更改并启动动画。

你喜欢/使用哪一个?我错过了任何选项吗?

附:如果解决方案能够提供先启动动画并在动画结束时反映值变化的可能性,那就太好了(但不是关键)。

【问题讨论】:

    标签: wpf data-binding animation mvvm storyboard


    【解决方案1】:

    好的,这是我经过一些实验后得出的结论。

    我创建了一个带有依赖属性的 Expression Blend 3 触发器(我将其命名为 Subscription)。我将 Subscription 绑定到与 TextBlock 绑定的相同值,并且此触发器附加到 Expression Blend 3 中的 ControlStoryboardAction。

    这里是触发器:

    public class DataTriggerPlus : TriggerBase<DependencyObject>
    {
        public static readonly DependencyProperty SubscriptionProperty =
            DependencyProperty.Register("Subscription", 
                typeof(string),
                typeof(DataTriggerPlus),
                new FrameworkPropertyMetadata("",
                  new PropertyChangedCallback(OnSubscriptionChanged)));
    
        public string Subscription
        {
            get { return (string)GetValue(SubscriptionProperty); }
            set { SetValue(SubscriptionProperty, value); }
        }
    
        private static void OnSubscriptionChanged(DependencyObject d,
          DependencyPropertyChangedEventArgs e)
        {
            ((DataTriggerPlus)d).InvokeActions(null);
        }
    }
    

    以下是它与情节提要的连接方式:

    <TextBlock x:Name="textBlock" Text="{Binding TestProp}" Background="White">
        <i:Interaction.Triggers>
            <local:DataTriggerPlus Subscription="{Binding TestProp}">
                <im:ControlStoryboardAction 
                    Storyboard="{StaticResource Storyboard1}"/>
            </local:DataTriggerPlus>
        </i:Interaction.Triggers>
    </TextBlock>
    

    我非常喜欢这种方法,Blend 3 设计师做得很好!

    编辑:回答 Drew 评论...

    是的,它与 Blend 一起提供。您可以只将 Microsoft.Expression.Interactions.dll 和 System.Windows.Interactivity 包含到您的项目中。

    是的,它很冗长(我问过是否有人想出了一个通过 Styles in this question 应用行为的好方法)——但也有灵活性的好处。例如,您不仅可以启动故事板,还可以从同一个触发器切换状态或执行其他操作。

    【讨论】:

    • &lt;i:Interaction.Triggers&gt; 来自哪里?我猜它与 Blend 一起提供。我们非 Blend 用户可以使用它吗?我想在我的代码中实现同样的目标,尽管我希望存在一个不那么冗长的解决方案,因为我有很多这样的实例需要这个。也许可以通过 Style...
    【解决方案2】:

    您可以创建一个触发器来启动动画。

    类似这样的:

    <Style>
        <Style.Triggers>
           <Trigger 
                Property="ViewModelProperty"
                Value="True">
                <Trigger.EnterActions>
                     <BeginStoryboard Storyboard="YourStoryBoard" />
                </Trigger.EnterActions>
           </Trigger>
        </Style.Triggers>
    </Style>
    

    至于动画完成后设置值的问题,这有点让人头疼。据我所知,您需要在情节提要上使用已完成的事件,这需要后面的代码,这是您希望使用 MVVM 避免的事情。

    我尝试使用 EventTriggers 绑定到已完成的事件,但这也带来了一些复杂性。详情请见here

    【讨论】:

    • 再一次,如果属性不是布尔值或一组选项,而是任意字符串或数字怎么办?
    • 那么您将使用 ValueConverter。相同的解决方案,只是使用 valueConverter
    • 除非您为每个单独的绑定创建一个值转换器,否则值转换器如何知道值已更改?
    • 如果值发生变化,它将再次通过转换器。转换器不知道或不需要知道除了它需要什么和返回什么之外的任何东西。
    • 您能否充实上面的示例代码来解释您在这些 cmets 中的答案?我想确切地了解使用值转换器对任意类型的视图模型属性进行动画更改是什么意思。谢谢。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-01-10
    • 1970-01-01
    • 2016-01-04
    • 1970-01-01
    相关资源
    最近更新 更多