WPF动画后元素Height绑定丢失问题及解决咨询
Hey,这个问题我遇到过好多次了——WPF动画的属性值优先级机制就是背后的“元凶”!
原因分析
当你用DoubleAnimation修改gridSlider的Height属性时,WPF会把动画的最终值直接“硬塞”给这个属性,而且默认情况下动画的FillBehavior是HoldEnd——这意味着动画结束后,它会一直“霸占”着这个属性的控制权。
在WPF的属性值优先级体系里,动画的优先级远高于普通数据绑定。所以哪怕你之前给Height绑好了gridMain的ActualHeight,只要动画还在保持着最终值(哪怕动画已经完成),绑定就会被完全忽略。这就是为什么窗口最大化时,gridMain的高度跟着变,但gridSlider的高度却固定不动——它还被动画的最终值锁着呢!
修复方案
最直接有效的办法就是在动画完成后,主动清除它对Height属性的控制,让原来的绑定重新接管。你已经给两个Storyboard加了Completed事件,刚好可以利用这个时机来操作:
步骤1:修改后台代码的Completed事件
在你的窗口代码文件里,找到两个Completed事件的处理方法,添加清除动画的代码:
private void ShrinkSlider_Completed(object sender, EventArgs e) { // 清除gridSlider.Height上的所有动画,让绑定重新生效 gridSlider.BeginAnimation(Grid.HeightProperty, null); } private void ExpandSlider_Completed(object sender, EventArgs e) { gridSlider.BeginAnimation(Grid.HeightProperty, null); }
为什么这能生效?
BeginAnimation方法传入null作为第二个参数时,会移除目标属性上所有已应用的动画。这时候,WPF会回到属性的下一个优先级值——也就是你最初设置的Height="{Binding ActualHeight, ElementName=gridMain}"绑定,这样gridSlider的高度就会再次跟着gridMain的高度动态变化了。
额外优化:调整动画的FillBehavior(可选)
如果你不想依赖Completed事件,也可以把Storyboard的FillBehavior设置为Stop:
<Storyboard x:Key="ShrinkSlider" x:Name="ShrinkSlider" FillBehavior="Stop"> <DoubleAnimation Storyboard.TargetProperty="Height" Storyboard.TargetName="gridSlider" DecelerationRatio="0.9" From="100" To="{Binding ActualHeight, ElementName=gridMain}" Duration="00:00:00.5" /> </Storyboard> <Storyboard x:Key="ExpandSlider" x:Name="ExpandSlider" FillBehavior="Stop"> <DoubleAnimation Storyboard.TargetProperty="Height" Storyboard.TargetName="gridSlider" DecelerationRatio="0.9" From="{Binding ActualHeight, ElementName=gridMain}" To="100" Duration="00:00:00.5" /> </Storyboard>
不过要注意:FillBehavior="Stop"会让动画结束后,属性值立刻回到动画开始前的那一刻的值——也就是动画启动时gridMain.ActualHeight的快照,而不是动态绑定的实时值。所以如果窗口在动画过程中改变了大小,这个方法可能不如清除动画的效果精准。因此更推荐第一种方法。
内容的提问来源于stack exchange,提问作者user10266595

