C# 为什么绑定不';你不能用动画吗?

C# 为什么绑定不';你不能用动画吗?,c#,.net,wpf,animation,binding,C#,.net,Wpf,Animation,Binding,我有一个动画绑定属性的简单问题。下面是一个简单的例子来说明这一点: 视图模型: public class ViewModel { private double myProperty; public double MyProperty { get { return myProperty; } set { myProperty = value; /* Break point here */ } } public ViewM

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

视图模型:

public class ViewModel
{

    private double myProperty;

    public double MyProperty
    {
        get { return myProperty; }
        set { myProperty = value; /* Break point here */ }
    }

    public ViewModel()
    {
        MyProperty = 20;
    }

}
public class MyControl : Button
{

    protected override void OnClick()
    {
        base.OnClick();
        Height = ActualHeight + 20;
    }

}
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>
MyControl:

public class ViewModel
{

    private double myProperty;

    public double MyProperty
    {
        get { return myProperty; }
        set { myProperty = value; /* Break point here */ }
    }

    public ViewModel()
    {
        MyProperty = 20;
    }

}
public class MyControl : Button
{

    protected override void OnClick()
    {
        base.OnClick();
        Height = ActualHeight + 20;
    }

}
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>
MyAnimatedControl:

public class ViewModel
{

    private double myProperty;

    public double MyProperty
    {
        get { return myProperty; }
        set { myProperty = value; /* Break point here */ }
    }

    public ViewModel()
    {
        MyProperty = 20;
    }

}
public class MyControl : Button
{

    protected override void OnClick()
    {
        base.OnClick();
        Height = ActualHeight + 20;
    }

}
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>
主窗口:

public class ViewModel
{

    private double myProperty;

    public double MyProperty
    {
        get { return myProperty; }
        set { myProperty = value; /* Break point here */ }
    }

    public ViewModel()
    {
        MyProperty = 20;
    }

}
public class MyControl : Button
{

    protected override void OnClick()
    {
        base.OnClick();
        Height = ActualHeight + 20;
    }

}
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>
公共部分类主窗口:窗口
{
公共主窗口()
{
DataContext=新的ViewModel();
初始化组件();
}
}
我继承了两个控件(MyControl和MyAnimatedControl),它们的高度属性绑定到ViewModel中的MyProperty属性(模式双向)

单击控件时,它们会增加其高度属性。MyControl为其指定一个新值,MyAnimatedControl为其设置动画

问题:

public class ViewModel
{

    private double myProperty;

    public double MyProperty
    {
        get { return myProperty; }
        set { myProperty = value; /* Break point here */ }
    }

    public ViewModel()
    {
        MyProperty = 20;
    }

}
public class MyControl : Button
{

    protected override void OnClick()
    {
        base.OnClick();
        Height = ActualHeight + 20;
    }

}
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>
当我单击其中一个MyControl时,绑定工作正常,所有控件的高度都会更新,断点在ViewModel中工作。但当我单击MyAnimatedControl时,绑定似乎不再工作:它单独增长,绑定不再工作,而两个正常控件仍然一起增长。它不再与其他控件共享相同的高度

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


谢谢。

控件.Height属性是一个
dependencProperty
属性,并且
dependencProperty
可以从多种来源进行设置,例如
样式设置器
动画
、源代码等。因此,微软必须定义一个优先顺序,以决定哪些更改
dependencProperty
值的可能方法比其他方法更重要

从逻辑上讲,正如您在示例中所做的那样,从
动画中设置
dependencProperty
,应该“覆盖”从其他来源(例如从属性设置器)设置的值。有关
dependencProperty
值优先级列表的完整信息,请参阅MSDN上的页面

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


更新>>>

哦,我想我知道你现在想要什么了。。。您想知道为什么在
动画
设置属性值动画时不调用属性设置器。但是,这不起作用,因为
动画
不会永久更改属性值。从页面的“数据绑定和动画制作”部分:

大多数动画属性可以是数据绑定或动画;例如,可以设置
双重动画
持续时间
属性的动画。但是,由于计时系统的工作方式,数据绑定或动画的行为与其他数据绑定或动画对象不同


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

最后,正如Rohit Vats在他的回答中所写的,您需要在已完成的事件处理程序中移除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);
}

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

如上所述,您可以通过三种方法解决此问题:

  • 将动画的FillBehavior设置为“停止”
  • 移除整个故事板
  • 从单个属性中删除动画
  • 在您的情况下,我们可以使用第三个选项在动画完成后从高度DP中删除动画


    其次,您应该调用方法来设置DP,它将更新绑定,从而更新绑定的ViewModel属性


    将所有这些内容放入双动画的
    事件中:

    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正在实现,并且属性更改事件是从
    MyProperty
    setter引发的。

    单击animatedcontrol时会发生什么?它能用一次吗?实际高度的值是多少?如果只单击普通控件,主窗口中的所有3个控件都会更新,并且都会增长。当您单击动画控件时,它会单独增长,绑定不再工作,而两个普通控件仍会一起增长。感谢这些有用的信息。是的,我一定不是很清楚。它们确实在每次单击时都会增长,但动画控件的高度(包含在ViewModel中)与两个普通控件的高度不同。@Max.您是说:a.FillBehavior=FillBehavior.HoldEnd@Core One我知道thos属性,但它对我解决这个问题没有帮助。@Sheridan+1是的,谢谢你的编辑,我认为我的问题来自于此。+1你的解决方案很好,但不适用于双向模式。不管怎样,这条线索很好。至少对我来说,它对双向也很有效。您是如何尝试的?如果您只是将单向ToSource模式替换为双向模式,则不会更改代码的行为(即,如果ViewModel的my属性已更新,则不会更改MyAnimatedControl的Height属性),您仍然会将
    Height
    属性单向绑定到视图模型(请参见我的编辑)。但是,如果由于以下原因而忽略VM属性,则返回初始值: