为什么绑定不能与动画一起工作?

本文关键字:一起 工作 动画 绑定 不能 为什么 | 更新日期: 2023-09-27 18:08:42

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

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);
    }
}

主窗口:

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属性被绑定(mode two way)到ViewModel中的MyProperty属性。

当控件被单击时,它们会增长其Height属性。MyControl给它赋一个新值,MyAnimatedControl使它动画化。

问题:

当我点击其中一个MyControl时,绑定工作正确,所有控件的高度更新,断点在ViewModel中工作。但是当我点击MyAnimatedControl时,绑定似乎不再工作了:它单独生长,绑定不再工作,而两个正常的控件仍然一起生长。它不再与其他控件共享相同的高度。

是否有一种方法可以对动画依赖属性进行操作绑定,从而在动画过程中更新ViewModel ?我读了这篇文章,但它没有回答。

谢谢。

为什么绑定不能与动画一起工作?

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

如本文所述,您可以使用三种方法解决此问题:
  1. 设置动画的FillBehavior为Stop
  2. 删除整个storyBoard。
  3. 从单个属性中移除动画。

在你的情况下,我们可以使用第三个选项在动画完成后从高度DP中删除动画。


第二,你应该调用SetCurrentValue方法来设置DP,它将更新绑定,从而更新绑定的ViewModel属性。
在<<p>把所有strong> 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 setter引发的

问题似乎是动画属性同时也是绑定的源。为了解决这个问题,你可以创建另一个高度属性(例如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);
}

Control.Height属性是一个DependencyPropertyDependencyProperty s可以从各种来源设置,例如:Style Setter, Animation,从代码等。由于这个原因,微软必须定义一个优先顺序来决定哪些可能的方法来改变DependencyProperty的值比其他的更重要。

从逻辑上决定,从Animation设置DependencyProperty,就像你在例子中所做的那样,应该"覆盖"从其他来源设置的值,例如。从属性设置器。有关DependencyProperty值优先级列表的完整信息,请参阅MSDN上的依赖属性值优先级页面。

但是,使用您的代码,我无法重现您的问题。我可以反复点击每一个Button,看到它的大小在不断增长。因此,如果您不能做到这一点,那么我怀疑您在某个地方有其他代码干扰了这一点。如果这不是你的问题,那么请清楚地解释什么是

更新>>>

哦,我想我现在明白你想要什么了…您想知道为什么当Animation动画属性值时不调用属性setter。然而,这将不起作用,因为Animation不会永久改变属性值。来自动画概述页面的数据绑定和动画动画部分:

大多数动画属性可以是数据绑定或动画;例如,您可以动画DoubleAnimationDuration属性。然而,由于计时系统的工作方式,数据绑定或动画动画的行为与其他数据绑定或动画对象不同。