WPF过多的PropertyChanged事件

时间:2010-08-12 17:04:48

标签: wpf events propertychanged

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

任何想法都非常感激。

此致 Fzzy

3 个答案:

答案 0 :(得分:3)

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

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

作为一个实际问题,你真正要做的只是提出一个告诉听众的事件“这个对象的一大堆属性已经改变了,但是我不打算告诉你关于它们的一个一个人。“当你说:

  

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

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

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

而且,在我看来,你的设计中缺少的是:应用领域的知识。

在第二个示例中,如果Fixture对象具有(比方说)依赖于FixtureStatus的值的十个属性,则引发十个属性更改事件可能看起来有点过分。也许是。也许该对象应该引发FixtureStatusChanged事件。然后,具有应用程序域知识的类可以监听此事件并忽略PropertyChanged事件。 (您仍然会在其他属性上引发PropertyChanged事件,以便知道FixtureStatusChanged事件意味着什么的对象可以保持最新 - 也就是说,如果它仍然是在您实施INotifyPropertyChanged后,您的班级必须实施FixtureStatusChanged。)

辅助评论:C#Universe中的大多数类,如果它们实现了引发Foo事件的方法,则调用该方法OnFoo。这是一个重要的约定:它使方法和事件之间的关系显式化,并且它使得调用方法的代码引发易于识别的事件。 Notify是一般方法的弱名称 - 通知谁?什么? - 在这种情况下,它实际上混淆了应该明确的东西。如果没有你的命名惯例隐瞒它正在发生的事实,财产变更通知就会非常棘手。

答案 1 :(得分:0)

忽略其他东西,我会说仅Notify(null)是一种不好的做法。它本身并不清楚这意味着什么,并且对于在5年后工作代码的开发人员可能会认为除非他们发表评论,否则它意味着其他事情。

答案 2 :(得分:0)

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

例如

双号 { 得到{return num;} 组 {  NUM =值;  OnPropertyChanged( “编号”);  OnPropertyChanged( “TwiceNumber”); } }

double TwiceNumber { 得到{return _num * 2.0;} }

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