【问题标题】:WPF excessive PropertyChanged eventsWPF 过多的 PropertyChanged 事件
【发布时间】:2010-08-12 17:04:48
【问题描述】:

通常在对象的属性设置器中,我们可能希望引发 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);
        }

任何想法都非常感谢。

问候, 呸呸呸

【问题讨论】:

  • 您想要关于常规属性或 DependencyProperty 的信息吗?
  • 如果不深入,该代码看起来有点吓人,试图描述该方法意图的大注释让我觉得很“臭”。你真正想做的是什么? Notification 事件的使用者应该决定在收到属性更改通知时他们需要做什么。
  • 一个例子是我们有一个 Fixture 对象,它有一个 FixtureStatus 属性。还有许多其他属性依赖 FixtureStatus 来确定它们的值。我认为 Notify 方法应该使用每个依赖属性的名称而不是全部通知来调用。代码开始变得有点复杂,但每个依赖属性的 Notify 调用。

标签: wpf events propertychanged


【解决方案1】:

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

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

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

我看到了将 PropertyChangedEventArgs 发送为 null 以指示对象的所有属性都已更改的实例。这似乎效率低下,并且似乎导致触发的事件比需要的多得多。

...实际意图恰恰相反。如果一个方法更改了对象的FooBarBazBat 属性,而该对象只有四个或五个属性,那么引发一个事件可能比引发四个更好。另一方面,如果对象有 60 个属性,那么引发四个事件可能会更好地使对象的每个侦听器——即使是那些没有查看这四个属性的侦听器——在他们关心的属性发生变化时执行他们所做的任何事情,因为那些属性没有。

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

在我看来,这就是您的设计所缺少的:应用领域知识。

在您的第二个示例中,如果Fixture 对象具有(比如说)十个取决于FixtureStatus 值的属性,那么引发十个属性更改事件似乎有点过分。也许是的。也许对象应该引发FixtureStatusChanged 事件。然后了解您的应用程序域的类可以监听这一事件并忽略PropertyChanged 事件。 (您仍然在其他属性上引发 PropertyChanged 事件,以便 知道 FixtureStatusChanged 事件含义的对象可以保持最新状态 - 也就是说,如果您的班级仍然需要它实施 INotifyPropertyChanged 后实施 FixtureStatusChanged。)

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

【讨论】:

    【解决方案2】:

    忽略其他内容,我会说单独使用 Notify(null) 是一种不好的做法。本质上并不清楚这意味着什么,并且对于在 5 年后工作代码的开发人员可能会认为这意味着其他东西,除非它们发生在 cmets 上。

    【讨论】:

      【解决方案3】:

      当我通过 setter 设置其他一些属性时,我遇到过计算属性(没有 setter)需要触发 PropertyChangeNotification 的情况。

      例如

      双数 { 得到 { 返回 num;} 放 { 数字=值; OnPropertyChanged("数字"); OnPropertyChanged("TwiceNumber"); } }

      双倍数 { 得到 {return _num * 2.0;} }

      作为一项规则,我只使用仅获取属性来执行此操作,我不明白为什么在这种情况下,一个属性触发另一个更改通知是不好的。但我想如果我为任何其他情况这样做,我很可能不知道我在做什么!

      【讨论】:

        猜你喜欢
        • 2010-11-23
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-11-03
        • 2021-11-27
        相关资源
        最近更新 更多