WPF 过多的 PropertyChanged 事件

标签 wpf events propertychanged

通常在对象的属性 setter 中,我们可能希望引发 PropertyChanged 事件,例如,

    public event PropertyChangedEventHandler PropertyChanged; 
    protected void Notify(string property)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(property));
        }
    }

    public string UserNote
    {
        get { return _userNote; }
        set
        {
            _userNote = value;
            Notify("UserNote"); 
        }
    }

在我们现有的代码库中,我看到 PropertyChangedEventArgs 被发送 null 的实例,以指示对象的所有属性都已更改。这似乎效率低下,并且似乎会导致触发比需要的事件多得多的事件。它似乎还会导致对象以循环方式相互更新的问题。

这是一个好的做法吗?

代码中的注释试图证明它的合理性......

//The purpose of this method is to wire up clients of NotificationBase that are also
//NotificationBases to *their* clients. Consider the following classes:     
         public class ClassA : NotificationBase
         {
             public int Foo
             {
                 get { return 123; }
                 set { Notify("Foo"); }
             }
         }

         public class ClassB : NotificationBase
         {
             ClassA A = new ClassA();
             public ClassB()
             {
                 A.PropertyChanged += AllChanged;
             }
             public void SetFoo()
             {
                 A.Foo = 456;
             }
         }

         public class ClassC
         {
             ClassB B = new ClassB();
             public ClassC()
             {
                 B.PropertyChanged += delegate { dosomething(); };
                 B.SetFoo(); // causes "dosomething" above to be called
             }
         }

        /// ClassB.SetFoo calls ClassA.Foo's setter, which calls ClassA.Notify("Foo").
        /// The event registration in ClassB's ctor causes ClassB.AllChanged to be called, which calls
        /// ClassB.Notify(null) - implying that ALL of ClassB's properties have changed.
        /// The event registration in ClassC's ctor causes the "dosomething" delegate to be called.
        /// So a Notify in ClassA is routed to ClassC via ClassB's PropertyChanged event.

        protected void AllChanged(Object sender, PropertyChangedEventArgs e)
        {
            Notify(null);
        }

非常感谢任何想法。

问候, 菲兹

最佳答案

这实际上是 PropertyChangedEventArgs 的设计(或其文档)存在问题。将 PropertyName 设置为 null 意味着“该对象上的所有属性都已更改”。但是,除非类是密封的,或者您使用反射,否则您实际上无法知道对象上的所有属性都已更改。您最多可以说的是对象基类中的所有属性都已更改。

在我的书中,这足以成为不使用此特定约定的理由,除非在极少数情况下我创建了实现属性更改通知的密封类。

作为一个实际问题,您真正想做的只是引发一个事件,告诉听众“该对象上的一大堆属性已更改,但我不会费心告诉您这些属性一个。”当你说:

I see instances where PropertyChangedEventArgs is being sent null in order to indicate that all properties of the object have changed. This seems inefficient and seems to lead to far more events being triggered than is needed.

...实际意图恰恰相反。如果方法更改对象的 FooBarBazBat 属性,并且该对象具有只有四、五处特性,筹集一项事件可能比筹集四项更好。另一方面,如果对象有 60 个属性,那么引发四个事件可能会更好,使对象的每个监听器(即使是那些不查看这四个属性的监听器)在他们关心的属性发生更改时执行他们所做的任何操作,因为这些属性没有。

问题在于,按照设计,属性更改通知系统并不是适合每项作业的足够细粒度的工具。它被设计为完全通用的,并且不了解内置的特定应用程序域。

在我看来,您的设计中缺少的是:应用程序领域知识。

在第二个示例中,如果 Fixture 对象具有(比如说)十个依赖于 FixtureStatus 值的属性,则引发十个属性更改事件可能看起来有点小过多的。也许是的。也许该对象应该引发一个 FixtureStatusChanged 事件。然后,了解您的应用程序域的类可以监听这一事件并忽略 PropertyChanged 事件。 (您仍然会在其他属性上引发 PropertyChanged 事件,以便知道 FixtureStatusChanged 事件含义的对象可以保持当前状态 -也就是说,在实现 FixtureStatusChanged 后,您的类是否仍然需要实现 INotifyPropertyChanged。)

第二条评论:C# 世界中的大多数类,如果它们实现了引发 Foo 事件的方法,请调用该方法 OnFoo。这是一个重要的约定:它使方法和事件之间的关系变得明确,并且使调用该方法的代码引发事件的事实易于识别。 Notify 是一般方法的弱名称 - 通知谁?什么? - 在这种情况下,它实际上混淆了一些应该明确的内容。属性更改通知已经够棘手的了,如果您的命名约定不掩盖它正在发生的事实。

关于WPF 过多的 PropertyChanged 事件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3470187/

相关文章:

c# - 从一个 UI(在网络上)更新数据后如何在其他 UI 上重新加载数据并保持关系?

javascript - 使用 jQuery 查找事件对象

C# 简单事件引发 - 使用 "sender"与自定义 EventArgs

java - SpinnerNumberModel 的 PropertyChangeSupport

c# - 在 WPF 项目中调用 Winforms ControlPaint.Light()

wpf - 带有水平项目的 ListView

c# - 如何在某些 ListBox 项目上使用不同的控件模板?

angular - 使用 Angular 指令捕获任何元素上的按键/按键/按键

c# - 监视 WPF 中 Telerik ScheduleView 控件的属性更改

c# - List<string> INotifyPropertyChanged 事件