【问题标题】:Triggering an animation from an event using MVVM使用 MVVM 从事件触发动画
【发布时间】:2011-10-19 16:59:39
【问题描述】:

我似乎在这里达到了某种 MVVM 断点。

当底层视图模型对象的“状态”属性发生变化时,我希望控件的不透明度动画半秒(DoubleAnimation 从 0.5 到 1.0)。我首先使用 DataTrigger 实现了这一点,但由于我还没有找到一种方法来对任何更改做出反应,只是给定的值,我必须在设置之前将 VM 对象的“状态”属性翻转为特殊的“待定”值达到其预期值。 (有没有办法对任何变化做出反应?)

这很 hacky,所以我开始摆弄 EventTriggers...

这是我迄今为止尝试过的:

  1. 使用普通的EventTrigger

这似乎需要一个 RoutedEvent,但这反过来又要求我的底层视图模型对象继承自 DependencyObject。

  1. 使用i:Interaction.Triggers

这样我可以收听正常的 .NET 事件并对其做出反应,但我还没有找到使用该方法启动 StoryBoard 的方法。

  1. 使用i:Interaction.Triggers 并写一个Behavior

我发现无法将我的自定义行为附加到它的关联控件这一事实上,这个实验失败了。

这就是 XAML 的样子:

   <cc:MyControl>
        <i:Interaction.Triggers>
            <i:EventTrigger EventName="Updated">
                <i:Interaction.Behaviors>
                    <cv:OpacityBehavior Duration="0:0:0:5" />
                </i:Interaction.Behaviors>
            </i:EventTrigger>
        </i:Interaction.Triggers>

这是自定义行为:

class OpacityBehavior : Behavior<MyControl>
{
    public Duration Duration { get; set; }

    protected override void OnAttached()
    {
        base.OnAttached();
        var animation = new DoubleAnimation(0.5, 1, Duration, FillBehavior.HoldEnd);
        var associatedObject = lookupVisualParent(this);
        associatedObject.BeginAnimation(UIElement.OpacityProperty, animation);
    }
}

这不起作用,因为 XAML 解析器要求它直接附加到“MyControl”,但我需要将它附加到事件触发器。然后我尝试了这种方法:

class OpacityBehavior : Behavior<DependencyObject>
{
    public Duration Duration { get; set; }

    protected override void OnAttached()
    {
        base.OnAttached();
        var animation = new DoubleAnimation(0.5, 1, Duration, FillBehavior.HoldEnd);
        var associatedObject = lookupVisualParent(this);
        associatedObject.BeginAnimation(UIElement.OpacityProperty, animation);
    }

    private UIElement lookupVisualParent(DependencyObject dObj)
    {
        if (dObj is UIElement)
            return (UIElement) dObj;

        if (dObj == null)
            return null;

        return lookupVisualParent(LogicalTreeHelper.GetParent(dObj));
    }
}

这失败了,因为lookupVisualParent 不起作用。行为的逻辑父级始终为null

我觉得这应该是一个相当普遍的任务?这个问题有好的解决方案吗?我觉得很奇怪我会编写我的视图模型类,以便它们派生自DependencyObject,以便在事件触发时启动动画。

干杯

【问题讨论】:

    标签: wpf events animation eventtrigger


    【解决方案1】:

    您可以简单地使用一个标志:在您的 VM 上设置一个名为“RecentlyChangedFlag”的标志。每当适当的值发生变化时,将其发送至true,然后发送至false。你可以这样做:

    private bool _changedFlag;
    public bool ChangedFlag
    {
        get
        {
            if (_changedFlag)
            {
                _changedFlag = false;
                OnPropertyChanged("ChangedFlag");
                return true;
            }
            // (else...)
            return false;
        }
        protected set
        {
            _changedFlag = value;
            OnPropertyChanged("ChangedFlag");
        }
    }
    

    即,当您想要通知动画开始时,使用上面的代码集ChangedFlag = true。 WPF查询真值后会重置为false。

    然后在RecentlyChangedFlag 的值为true 时出现动画,例如EnterAction

    希望对您有所帮助。

    【讨论】:

    • 谢谢,但这就是我最初“解决”它的方式(使用 DataTrigger),但是,正如我在 OP 中指出的那样,这很糟糕。我对 WPF 相当陌生,所以这实际上可能是从 VM 触发行为/动画的正常方式,但对我来说,这似乎是在解决不应该存在的限制。会不会是你根本无法从 VM 中的事件触发动画?
    • ViewModel 的存在是为了将模型转换为视图可以使用的东西。因此,它是做这种事情的理想场所。这不是骇人听闻的 IMO 和一个指示重要内容已更改的标志应该存在于 VM 中。只要 ViewModel 自己站起来,这不是问题。一个 ViewModel 有一个标志来指示你谈论的任何“事件”——如果你把它称为正确的东西,它似乎并不过分?
    • 我不会对此深信不疑,但它不可能从事件中触发动画似乎很奇怪。毕竟,事件是用来表示状态变化的。必须添加第二个属性只是为了向第一个表示状态更改是一种解决方法恕我直言。我很惊讶地看到 WPF 提供的工具如此不足。在这种特殊情况下,我当然可以接受“状态属性解决方案”,但我设想我的 VM 类将来会充满这些属性。一定有更好的方法吧?
    猜你喜欢
    • 2010-11-05
    • 1970-01-01
    • 1970-01-01
    • 2012-10-31
    • 2020-03-25
    • 2012-07-28
    • 2014-05-12
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多