通过Storyboard更改时,数据绑定不会更新?

时间:2011-12-09 11:36:03

标签: c# wpf binding storyboard

我有一个 MyBool 类的实例,其属性为 IsTrue ,存储为 StaticResource 。我还有一个CheckBox,其IsChecked属性绑定到类的实例。

{Binding IsTrue, Mode=TwoWay, Source={StaticResource MyBoolInstance}}

工作正常。如果我在CheckBox上更改check属性, MyBool 的实例也会更新,反之亦然。

但是,如果我操纵CheckBox 的IsChecked属性一个StoryBoard

<Storyboard x:Key="ColourToggle">  
  <ObjectAnimationUsingKeyFrames 
        Storyboard.TargetProperty="IsChecked"  
        Storyboard.TargetName="ThisCheckBox">  
    <DiscreteObjectKeyFrame KeyTime="0">  
      <DiscreteObjectKeyFrame.Value>  
          <System:Boolean>True</System:Boolean>  
      </DiscreteObjectKeyFrame.Value>  
    </DiscreteObjectKeyFrame>  
  </ObjectAnimationUsingKeyFrames>  
</Storyboard>

MyBool 实例的 IsTrue 属性不会更新!

有任何建议或解决方法吗?

2 个答案:

答案 0 :(得分:3)

以下是完全重现问题的示例:

<StackPanel>
    <StackPanel.Resources>
        <l:MyBool x:Key="MyBool" IsTrue="False" />
    </StackPanel.Resources>
    <CheckBox x:Name="myCheckBox"
              Content="{Binding RelativeSource={RelativeSource Mode=Self}, Path=IsChecked}"
              IsChecked="{Binding Source={StaticResource MyBool}, Path=IsTrue, Mode=TwoWay}"
              HorizontalAlignment="Center"
              VerticalAlignment="Top">
        <CheckBox.Triggers>
            <EventTrigger RoutedEvent="UIElement.MouseEnter">
                <BeginStoryboard  x:Name="isCheckedBeginStoryboard">
                    <Storyboard>
                        <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="IsChecked">
                            <DiscreteObjectKeyFrame KeyTime="0">
                                <DiscreteObjectKeyFrame.Value>
                                    <System:Boolean>True</System:Boolean>
                                </DiscreteObjectKeyFrame.Value>
                            </DiscreteObjectKeyFrame>
                        </ObjectAnimationUsingKeyFrames>
                    </Storyboard>
                </BeginStoryboard>
            </EventTrigger>
            <EventTrigger RoutedEvent="UIElement.MouseLeave">
                <StopStoryboard BeginStoryboardName="isCheckedBeginStoryboard" />
            </EventTrigger>
        </CheckBox.Triggers>
    </CheckBox>
    <CheckBox Content="Also two way binding to MyBool.IsTrue no animation" IsChecked="{Binding Source={StaticResource MyBool}, Path=IsTrue}" />
    <TextBlock Text="{Binding Source={StaticResource MyBool}, Path=IsTrue, StringFormat={}MyBool.IsTrue: {0}}" />
    <TextBlock Text="{Binding ElementName=myCheckBox, Path=IsChecked, StringFormat={}myCheckBox.IsChecked: {0}}" />
</StackPanel>

MyBool是一个同样实现INotifyPropertyChanged的简单类:

public class MyBool : INotifyPropertyChanged
{
    private bool _isTrue;
    public bool IsTrue
    {
        get { return _isTrue; }
        set
        {
            if (_isTrue != value)
            {
                _isTrue = value;
                NotifyPropertyChanged("IsTrue");
            }
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    private void NotifyPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
            PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

正如您在运行此操作时所看到的,当动画处于活动状态时,您的StaticResource未被更新 - 当动画未激活时,它就是。这是因为动画运行时WPF为IsChecked属性提供了一个新值(由Storyboard定义)。这有效地破坏了旧的价值 - 双向BindingStaticResource。动画完成并停止后,WPF会将旧值IsChecked恢复为原始绑定表达式,因此您的资源MyBool将继续接收更新。

可以在此处找到关于DependencyProperty值优先级的精彩文章:

http://msdn.microsoft.com/en-us/library/ms743230.aspx

希望这有帮助!

答案 1 :(得分:1)

问题在于绑定,而不是在UI中。 您可以尝试在代码隐藏ThisCheckBox.IsChecked = true;中设置IsChecked,如果您在XAML中绑定,这将被忽略!

XAML:

<Window x:Class="WpfApplication3.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<Grid>
    <CheckBox x:Name="ThisCheckBox" IsChecked="{Binding IsTrue}" />
</Grid>

代码:

public partial class Window1 : Window
{
    public Window1()
    {
        InitializeComponent();

        this.DataContext = new CData();

        ThisCheckBox.ClearValue(CheckBox.IsCheckedProperty);
        //next will work only after clear binding
        ThisCheckBox.IsChecked = true;
    }
}

public class CData : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    private bool _isTrue;
    public bool IsTrue
    {
        get { return _isTrue; }
        set
        {
            if (_isTrue != value)
            {
                _isTrue = value;
                OnPropertyChanged(new PropertyChangedEventArgs("IsTrue"));
            }
        }
    }

    protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null)
        {
            handler(this, e);
        }
    }
}