为什么绑定不适用于动画?

时间:2014-06-16 09:34:28

标签: c# .net wpf animation binding

我对动画绑定属性有一个简单的问题。这是一个简单的例子来说明它:

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,但没有回答。

感谢。

3 个答案:

答案 0 :(得分:3)

问题是动画具有更高的优先顺序,因此当它应用于任何属性时,该属性中的后续更改将不会反映在UI和绑定中。

正如here所述,您可以通过三种方法解决这个问题:

  1. 将动画的FillBehavior设置为停止。
  2. 删除整个storyBoard。
  3. 从单个属性中删除动画。
  4. 在您的情况下,我们可以在动画完成后使用第三个选项从高度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属性是DependencyPropertyDependencyProperty可以从各种来源设置,例如。 Style SetterAnimation,来自代码等。由于这个原因,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);
}