我对动画绑定属性有一个简单的问题。这是一个简单的例子来说明它:
ViewModel:
public class ViewModel
{
private double myProperty;
public double MyProperty
{
get { return myProperty; }
set { myProperty = value; /* Break point here */ }
}
public ViewModel()
{
MyProperty = 20;
}
}
MyControl:
public class MyControl : Button
{
protected override void OnClick()
{
base.OnClick();
Height = ActualHeight + 20;
}
}
MyAnimatedControl:
public class MyAnimatedControl : Button
{
protected override void OnClick()
{
base.OnClick();
DoubleAnimation a = new DoubleAnimation(ActualHeight + 20, new Duration(TimeSpan.FromMilliseconds(500)));
this.BeginAnimation(HeightProperty, a);
}
}
MainWindow:
public partial class MainWindow : Window
{
public MainWindow()
{
DataContext = new ViewModel();
InitializeComponent();
}
}
<Grid>
<StackPanel>
<local:MyAnimatedControl Height="{Binding MyProperty, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Content="Animated"/>
<local:MyControl Height="{Binding MyProperty, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Content="Normal 1"/>
<local:MyControl Height="{Binding MyProperty, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Content="Normal 2"/>
</StackPanel>
</Grid>
我有两个从Button继承的控件(MyControl和MyAnimatedControl),它们的Height属性被绑定(模式为双向)到ViewModel中的MyProperty属性。
单击控件后,它们会增加其Height属性。 MyControl为其分配一个新值,MyAnimatedControl为其设置动画。
问题:
当我单击其中一个MyControl时,绑定正常工作,所有控件的高度都会更新,断点在ViewModel中工作。但是,当我单击MyAnimatedControl时,绑定似乎不再起作用:它单独生长,绑定不再起作用,而两个正常控件仍在一起生长。它不再与其他控件共享相同的高度。
有没有办法对动画依赖属性进行操作绑定,这将在整个动画中更新ViewModel?我看了this post,但没有回答。
感谢。
答案 0 :(得分:3)
问题是动画具有更高的优先顺序,因此当它应用于任何属性时,该属性中的后续更改将不会反映在UI和绑定中。
正如here所述,您可以通过三种方法解决这个问题:
在您的情况下,我们可以在动画完成后使用第三个选项从高度DP中删除动画。
其次,您应该调用SetCurrentValue方法来设置将更新绑定的DP,从而更新绑定的ViewModel属性。
将所有这些内容放在 Completed
的 DoubleAnimation
事件中:
public class MyAnimatedControl : Button
{
protected override void OnClick()
{
base.OnClick();
DoubleAnimation a = new DoubleAnimation(ActualHeight + 20,
new Duration(TimeSpan.FromMilliseconds(500)));
a.Completed += (s, e) =>
{
BeginAnimation(Button.HeightProperty, null); // Remove animation.
SetCurrentValue(Button.HeightProperty, ActualHeight); // Set value.
};
this.BeginAnimation(Button.HeightProperty, a);
}
}
另外,请确保您的ViewModel正在实施INotifyPropertyChanged,并且已从 MyProperty
设置器中引发属性更改事件。
答案 1 :(得分:2)
Control.Height
属性是DependencyProperty
,DependencyProperty
可以从各种来源设置,例如。 Style Setter
,Animation
,来自代码等。由于这个原因,Microsoft必须定义优先顺序,以决定更改DependencyProperty
值的哪些方法更重要其他
逻辑上决定从您的示例中设置DependencyProperty
Animation
应该“覆盖”从其他来源设置的值,例如。来自物业设定者。有关DependencyProperty
值优先级列表的完整信息,请参阅MSDN上的Dependency Property Value Precedence页面。
但是,使用您的代码,我无法重现您的问题。我可以反复点击每一个Button
并看到它的大小不断增长。因此,如果你不能这样做,那么我怀疑你有一些其他代码干扰这个。如果那是不你的问题,那么请清楚地解释一下是什么。
更新&gt;&gt;&gt;
哦,我想我现在看到了你的内容......你想知道为什么在Animation
动画属性值时不会调用你的属性设置器。但是,这不起作用,因为Animation
不会永久更改属性值。来自Animation Overview页面的数据绑定和动画动画部分:
大多数动画属性可以是数据绑定或动画;例如,您可以为
Duration
的{{1}}属性设置动画。但是,由于计时系统的工作方式,数据绑定或动画动画的行为与其他数据绑定或动画对象不同。
答案 2 :(得分:2)
问题似乎是动画属性同时是绑定的来源。要解决这个问题,您可以创建另一个高度属性(例如MyHeight
),只要控件的高度发生变化,该属性就会更新,并作为OneWayToSource
绑定的目标。
public class MyAnimatedControl : Button
{
public static readonly DependencyProperty MyHeightProperty =
DependencyProperty.Register(
"MyHeight", typeof(double), typeof(MyAnimatedControl));
public double MyHeight
{
get { return (double)GetValue(MyHeightProperty); }
set { SetValue(MyHeightProperty, value); }
}
protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo)
{
base.OnRenderSizeChanged(sizeInfo);
MyHeight = sizeInfo.NewSize.Height;
}
}
绑定:
<local:MyAnimatedControl ...
MyHeight="{Binding MyProperty, Mode=OneWayToSource}"
Height="{Binding MyProperty, Mode=OneWay}"/>
为了更新其他绑定目标,您还需要在视图模型中实现INotifyPropertyChanged
接口:
public class ViewModel : INotifyPropertyChanged
{
private double myProperty = 30;
public double MyProperty
{
get { return myProperty; }
set
{
myProperty = value;
RaisePropertyChanged("MyProperty");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
最后,正如Rohit Vats在他的回答中所写的那样,你需要在Completed事件处理程序中的Height属性中删除动画的保留:
protected override void OnClick()
{
base.OnClick();
DoubleAnimation a = new DoubleAnimation(ActualHeight + 20,
new Duration(TimeSpan.FromMilliseconds(500)));
a.Completed += (s, e) =>
{
BeginAnimation(Button.HeightProperty, null); // Remove animation.
SetCurrentValue(Button.HeightProperty, ActualHeight); // Set value.
};
this.BeginAnimation(Button.HeightProperty, a);
}