biz*_*zon 6 c# wpf animation dependency-properties
我有问题是杀了我.以下是简单的例子
<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>
Run Code Online (Sandbox Code Playgroud)
在代码背后
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);
}
Run Code Online (Sandbox Code Playgroud)
应用看起来像这样

按_resizeButton后立即左键放大.然后我按_animateButton - 左键慢慢放大.在此之后,我再次按_resizeButton,没有任何事情发生.这是为什么?
我注意到同样的事情是动画Top属性
Ian*_*ths 16
要了解行为,您需要了解动画在WPF属性系统中的工作原理:通过将属性设置为不同的值,动画不起作用.他们通过为暂时优先于'基础'值的财产提供有效价值来工作.这是一个微妙的区别,但它正是导致你在这里失败的原因.
当第一次遇到动画系统时,大多数人都会想象它通过反复调用属性的"set"访问器来工作.但它没有 - 如果您在开始动画之前设置属性,则您设置的原始值仍然存在.只是getter将返回动画系统提供的值,而不是返回'local'值.实际上,您甚至可以通过在动画运行时设置属性来更改"本地"值,但在动画停止之前,该局部值将不会显示.
实际上,财产系统做了很多这样的事情 - 它不仅仅是动画.此帮助主题列出了属性值可能来自的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;
}
Run Code Online (Sandbox Code Playgroud)
我在这里改变的是代码不再试图猜测动画系统结束的位置 - 这只是读取动画系统设置属性的任何值并使其成为新的本地值.我更喜欢到+= 10在Completed处理程序,这感觉就像逻辑稍微脆弱的重复给我.
处理它的另一种方法是在尝试更新属性时删除动画:
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);
}
Run Code Online (Sandbox Code Playgroud)
这对我来说似乎稍微强一些,因为即使动画在单击调整大小按钮时尚未完成,这也会起作用.