【问题标题】:Why after animating height setting this property directly stops working为什么在设置高度动画后直接停止工作
【发布时间】:2011-01-05 01:05:41
【问题描述】:

我有问题,这让我很生气。下面是一个简单的例子

<Grid Name="_grid">
    <Button Content="Button" Height="23" HorizontalAlignment="Left" Margin="36,33,0,0" Name="button" VerticalAlignment="Top" Width="75" />
    <Button Content="Enlarge through animation" Height="23" Margin="172,33,24,0" Name="_animateButton" VerticalAlignment="Top" Click="_animateButton_Click" />
    <Button Content="Enlarge through simple heigh changing" Height="23"  Margin="172,62,24,0" Name="_resizeButton" VerticalAlignment="Top" Click="_resizeButton_Click" />
</Grid>

在后面的代码中

private void _resizeButton_Click(object sender, RoutedEventArgs e)
{
    button.Height += 10;
}

private void _animateButton_Click(object sender, RoutedEventArgs e)
{
    Storyboard storyboard = new Storyboard();

    DoubleAnimation animation = new DoubleAnimation(button.Height + 10, new Duration(new TimeSpan(0, 0, 0, 1)));
    storyboard.Children.Add(animation);

    Storyboard.SetTargetName(animation, button.Name);
    Storyboard.SetTargetProperty(animation, new PropertyPath(HeightProperty));

    storyboard.Begin(_grid);
}

应用程序看起来像这样

按下_resizeButton左键后立即放大。然后我按下 _animateButton - 左按钮正在缓慢放大。在此之后,我再次按下 _resizeButton 并没有发生任何事情。这是为什么呢?

我注意到在设置 Top 属性动画时也是如此

【问题讨论】:

    标签: c# wpf animation dependency-properties


    【解决方案1】:

    要了解这种行为,您需要了解动画在 WPF 的属性系统中的工作方式的一个特性:随着时间的推移将属性设置为不同的值,动画不会起作用。它们通过为暂时优先于“基本”值的属性提供有效值来工作。这是一个微妙的区别,但正是它导致你在这里卡住了。

    当第一次遇到动画系统时,大多数人认为它是通过重复调用属性的“set”访问器来工作的。但它没有 - 如果您在开始动画之前以这种方式设置属性,那么您设置的原始值仍然存在。只是 getter 将返回动画系统提供的值,而不是返回“本地”值。事实上,您甚至可以通过在动画运行时设置属性来更改“本地”值,但在动画停止之前,该本地值不会变得可见。

    实际上,属性系统做了很多这样的事情——不仅仅是动画。这个help topic 列出了房产价值可能来自的 11 个不同的地方。动画是第二高优先级。通过“set”访问器(或通过 Xaml 中的属性)以通常方式设置的属性是次高优先级,但您可以看到模板、样式和触发器都为没有本地属性值时提供其他来源。

    WPF 中的动画系统有一个“基础”值的概念,它本质上是当前动画值之后可用的次高优先级值。如果您有本地值,那将是基值,但如果您没有,则基值将来自该文章中列出的其他来源之一。

    所有这一切的结果是,没有一种简单的方法可以做你似乎想做的事情。我认为您想要的是让动画运行到完成,并让属性保留其最终动画值,直到您将本地值设置为其他值。

    如果您让动画在完成后停止,则有效值将恢复为基本值。 (正如您在评论中所说,动画完成后它会收缩。)如果您告诉动画在完成后保持(这是默认行为),那么动画将永远提供一个比本地值具有更高优先级的值,这就是您看到手动调整按钮大小不再起作用的原因。所以这两个选项都不能满足你的要求。

    有两种方法可以解决这个问题。 @responderNS5 贴了一个——处理动画的完成,修改局部值以反映动画的最终值,然后停止动画。 (您可以认为这是对属性的一种降级 - 它将它从高优先级但瞬态动画提供的属性值转换为优先级稍低但更持久的本地值。)我倾向于修改代码一点:

    private void myDoubleAnimation_Completed(object sender, EventArgs e)
    {
        // Animation complete, but holding, so Height will currently return
        // return the final animated value.
        double finalHeight = button.Height;
    
        // Remove the animation.
        button.BeginAnimation(Button.HeightProperty, null);
    
        // Modify the local value to be the same as the final animated value.
        button.Height = finalHeight;
    }
    

    我在这里所做的更改是,代码不再试图猜测动画系统的最终位置——它只是读取动画系统设置属性的任何值,并将其设为新的本地值。与Completed 处理程序中的+= 10 相比,我更喜欢它,这对我来说感觉像是有点脆弱的逻辑重复。

    处理它的另一种方法是在您尝试更新属性时删除动画:

    private void _resizeButton_Click(object sender, RoutedEventArgs e)
    {
        double currentHeight = button.Height;
        button.BeginAnimation(Button.HeightProperty, null);
        button.Height = currentHeight + 10;
    }
    
    private void _animateButton_Click(object sender, RoutedEventArgs e)
    {
        DoubleAnimation animation = new DoubleAnimation
        {
            By = 10,
            Duration = new Duration(TimeSpan.FromSeconds(1))
        };
        button.BeginAnimation(Button.HeightProperty, animation);
    }
    

    这对我来说似乎更健壮一些,因为即使在您单击调整大小按钮时动画还没有完成,这也会起作用。

    【讨论】:

      【解决方案2】:

      您需要将DoubleAnimationFillBehavior 设置为Stop 而不是Hold。见这篇文章:

      【讨论】:

      • 感谢您的回答,但在设置 animation.FillBehavior = FillBehavior.Stop 后将其收缩回最后的高度值。我做错了吗?
      【解决方案3】:

      Rick 的回答不会像您指出的那样起作用。在您的情况下,您需要将动画属性(按钮的高度)的动画设置为 null,以便您可以对此属性进行进一步修改。

      http://msdn.microsoft.com/en-us/library/aa970493.aspx

          private void _animateButton_Click(object sender, RoutedEventArgs e) 
          {
              DoubleAnimation animation = new DoubleAnimation();
              animation.By = 10;
              animation.Duration = new Duration(TimeSpan.FromSeconds(1));
              animation.Completed += new EventHandler(myDoubleAnimation_Completed);
              button.BeginAnimation(Button.HeightProperty, animation);
          }
      
          private void myDoubleAnimation_Completed(object sender, EventArgs e)
          {
              button.BeginAnimation(Button.HeightProperty, null);
              button.Height += 10; 
          }
      

      将您的 _animateButton_Click 处理程序更改为上面指示的处理程序。另外,添加第二个事件处理程序,以便在动画完成时,我们可以删除对“动画”的引用 myDoubleAnimation_Completed 中的第二条语句是因为当动画引用被删除时,非动画值又出现了。

      【讨论】:

      • 完美.. 还有其他一些帖子对此没有答案。我会把你的答案的链接放上去
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-05-23
      • 1970-01-01
      • 1970-01-01
      • 2012-07-12
      • 2023-03-22
      • 1970-01-01
      相关资源
      最近更新 更多