【问题标题】:What triggers change notification in DependencyProperties?什么触发 DependencyProperties 中的更改通知?
【发布时间】:2014-07-24 21:58:08
【问题描述】:

我正在探索 C# 中的数据绑定,并想了解对属性(由依赖项属性支持)的哪些更改实际上会触发对绑定目标的更新通知。

为了测试,我使用了一个简单的类设置,我可以在其中测试对嵌套属性(子属性)的更改:

// Person with a name
public class Person
{
    public String Name { get; set; }
}

// Class giving the best friend as DependencyProperty
public class Friends : DependencyObject
{
    public static DependencyProperty BestFriendProperty =
        DependencyProperty.Register("BestFriend", typeof(Person), typeof(Friends));

    public Person BestFriend
    {
        get { return (Person)this.GetValue(BestFriendProperty); }
        set { this.SetValue(BestFriendProperty, value); }        
    }
}

现在我将 BestFriendProperty(作为源)绑定到另一个类的某个 Person 属性。我希望通过此绑定的更新通知仅在我使用 BestFriend 的设置器时才有效:

myBoundFriends.BestFriend = new Person(); // myBoundFriends is of type Friends

但我发现,即使是(嵌套的)名称属性的直接更改也会触发通知并同步到绑定的目标:

myBoundFriends.BestFriend.Name = "Otto"; // why does this trigger update?

数据绑定对我来说似乎有些神秘。实际上我认为只有 Freezable 对象(在 WPF 中广泛使用)能够在其任何子属性更改时触发更新?!


编辑:当我将一个简单的人到人转换器添加到绑定时,通知机制按预期工作:

public class PersonPersonConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        Person p = (Person)value;
        return new Person(String.Copy(p.Name));
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        Person p = (Person)value;
        return new Person(String.Copy(p.Name));
    }
}

请注意,转换器通过复制 Name 字符串对 Person 对象进行深层复制。我想这可能是解决方案的相关点。

【问题讨论】:

  • 您问题中的 Person.Name 属性不会触发其来源为 Friends.BestFriend 的绑定的更新。您的代码中还有其他原因导致更新,并且您错误地认为是通过将“Otto”分配给 Name 属性来触发的,我猜...
  • 是的,这正是重点。我以为已经理解了数据绑定,但是这个测试用例让我感到困惑。您是否有进一步的想法或提示如何调试场景以了解发生了什么?
  • 您将需要检查与这些数据绑定相关和/或可以创建 Person 对象的所有源代码(并研究在什么条件下会发生这种情况)。如果梳理源代码太难或者太麻烦,可以在Person的构造函数中设置断点来跟踪谁在什么时候创建了Person对象。如果您没有为 Person 声明构造函数,为了排除故障,请实现一个空的默认构造函数,您可以在该构造函数上设置断点。
  • 如果你发现你的问题与 Person 对象的创建无关,那么你需要彻底研究你的源代码和程序流程——至少有尽可能多的不同可能性。代码可以做,因为有不同的方法给猫剥皮......
  • @elgonzo:添加一个对 Person 对象进行深层复制的 Person to Person 转换器使通知机制按预期工作,并且子属性更改不再触发更新,但我没有明白为什么?!

标签: c# wpf data-binding


【解决方案1】:

这是你的问题,你的数据模型没有实现 INotifyPropertyChanged 接口。

public class Person
{
    public String Name { get; set; }
}

改成

public class Person : INotifyPropertyChanged
{
    private string name;

    public String Name
    {
        get { return name; }
        set
        {
            if (value == name) return;
            name = value;
            OnPropertyChanged();
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    [NotifyPropertyChangedInvocator] // Comment out this attribute if you don't have R#
    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
    }
}

这将向 GUI 发出信号,以在您的代码中更改绑定到您的 Name 属性的任何内容时重新绘制它。或者,您可以使用 DO 作为基类并将 Name 属性设置为 DP,但这只是矫枉过正。我通常有一个基类来实现我的视图模型和数据模型的接口。

是的,我是一个重度瘾君子;)

【讨论】:

  • 我猜你误解了我的问题。在这两种情况下我都会收到更改通知:通过绑定 DP 的设置器进行更改和更改绑定 DP 的子属性。我只希望通过更改设置器在第一种情况下触发通知!
  • 显示您的 xaml。从您的代码看来,您正在绑定到 Person 类的 Name 属性,如上所述,它没有 INotifyPropertyChanged 或(等等)使 Person 数据模型成为带有 DP 的 DO。如果不这样做,当 Name 属性更改时,您的 UI 将不会发出信号……您必须对所有嵌套类执行此操作!或者非常懒惰并通过手动调用 IE PropertyChanged(this,new PropertyChangedEventArgs(String.Empty) 来使代码混乱,这将重新评估每个绑定并强制重绘。
  • 我这里没有任何 XAML 代码,这是一个最小的非 UI 测试用例。
猜你喜欢
  • 1970-01-01
  • 2012-03-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-24
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多