【问题标题】:Remove storyboard but keep animated value?删除故事板但保留动画价值?
【发布时间】:2010-12-13 15:20:34
【问题描述】:

如何在 XAML 中删除情节提要(即 DataTrigger 中的 RemoveStoryboard 操作)但保留动画的值。类似于Animatable.BeginAnimation:

如果动画的 BeginTime 为 null,则所有当前动画都将被移除并保留属性的当前值。

【问题讨论】:

    标签: wpf animation storyboard datatrigger


    【解决方案1】:

    RemoveStoryboard 的主要用途是删除动画值并将它们设置回未动画状态。在大多数情况下,您可以将调用切换到 PauseStoryboard 或 StopStoryboard,具体取决于具体情况。唯一的例外是当您需要释放情节提要所占用的资源或将其用于其他目的时。

    如果你真的想移除情节提要并保留属性值,你必须直接在属性上设置动画值。这可以通过将每个值设置为动画值来完成,如下所示:

    void CopyAnimatedValuesToLocalValues(DependencyObject obj)
    {
      // Recurse down tree
      for(int i=0; i<VisualTreeHelper.GetChildrenCount(obj); i++)
        CopyAnimatedValuesToLocalValues(VisualTreeHelper.GetChild(obj, i));
    
      var enumerator = obj.GetLocalValueEnumerator();
      while(enumerator.MoveNext())
      {
        var prop = enumerator.Current.Property;
        var value = enumerator.Current.Value as Freezable;
    
        // Recurse into eg. brushes that may be set by storyboard, as long as they aren't frozen
        if(value!=null && !value.IsFrozen)
          CopyAnimatedValuesToLocalValues(value);
    
        // *** This is the key bit of code ***
        if(DependencyPropertyHelper.GetValueSource(obj, prop).IsAnimated)
          obj.SetValue(prop, obj.GetValue(prop));
    
      }
    }
    

    在删除情节提要之前调用此函数以复制动画值。

    编辑有评论指出此代码可能是不必要的,因为使用 BeginTime=null 调用 BeginAnimation 可以达到类似的效果。

    虽然 BeginTime=null 的 BeginAnimation 确实使值看起来像是复制到本地,但稍后调用 RemoveStoryboard 将导致值恢复。这是因为 BeginTime=null 的 BeginAnimation 会导致之前的动画在新动画开始之前保持其值,但不会影响本地值。

    上面的代码实际上覆盖了本地值,所以所有的动画都可以被移除,并且对象仍然有它们的新值。所以如果你真的想调用 RemoveStoryboard 并且仍然保留你的值,你将需要我上面写的代码或类似的东西。

    【讨论】:

    • 正如我在问题中提到的,我想在 XAML 中使用它。这里的主要目标是基于以当前值开始的 DataTrigger 启动新动画。据我所知,AFAIK 无法通过暂停或停止当前情节提要来实现,因为它会保持或重置当前值。另请注意,WPF 中不需要您的代码。您可以简单地使用将 BeginTime 设置为 null 的动画调用 Animatable.BeginAnimation。将值复制到本地具有相同的效果。
    • 我不确定我是否理解您的需求。如果你只是想开始一个新的动画,为什么不直接这样做而不调用 RemoveAnimation 动作呢? WPF 旨在从该情况下的当前值开始。不是这样吗?
    • 用 BeginTime=null 调用 BeginAnimation 和我的代码效果是不一样的。具体来说,它不会将值复制到本地。如果您使用 BeginTime=null 调用 BeginAnimation,然后再调用 RemoveAnimation,则这些值将恢复为原始值,因为本地值从未更改过。但是,如果您使用我的代码,这些值将保持不变,因为它们实际上是复制到本地的。我正在解释即使在 RemoveAnimation 之后保持本地值的必要条件,并且我编写的代码是必要的。这有意义吗?
    • 你说得对,不一样。关于您的问题:如果我在开始新的故事板之前删除了故事板,则动画值会跳回默认(未动画)值。我不希望这种情况发生我想从其他动画停止的值开始。另外我需要一个仅 XAML 的解决方案。但是使用 Datatrigger(这似乎是基于 ViewModel 状态触发动画的唯一方法)我总是必须删除 DataTrigger.ExitAction 中的旧动画。
    • 让我们做一个简单的练习(这似乎很难完成):编写一个数据模板,当绑定的 ViewModel 的状态变为“活动”时,它的颜色会慢慢变为绿色。当状态变为“非活动”时,它会从当前具有的任何颜色开始将其颜色设置为灰色。当状态更改为“未知”时,开始将其颜色设置为黄色,再次从对象当前具有的任何颜色开始。这是一个如此简单的场景,但它真的让我很难过。我倾向于认为这是 WPF 动画系统的不足之处。
    【解决方案2】:

    我在使用AnimationTimeline 时遇到了类似的问题。最简单的解决方案是在代码后面和之前使用 null 参数调用 BeginAnimation 之前在代码中捕获 Completed 事件以删除动画,获取属性的当前值并使用它来设置它。

    这会获取最后一个动画值,并设置它。

    void OnCompleted( object sender, EventArgs args )
    {
        // Required to copy latest animated value to local value.
        o.SomeValue = o.SomeValue;
        o.BeginAnimation( SomeClass.SomeValueProperty, null );
    }
    

    【讨论】:

      猜你喜欢
      • 2011-03-11
      • 2012-02-21
      • 1970-01-01
      • 2021-05-05
      • 2020-05-19
      • 2019-10-27
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多