为嵌套(子)对象订阅INotifyPropertyChanged

thm*_*shd 30 .net c# events inotifypropertychanged

我正在寻找一个干净而优雅的解决方案来处理INotifyPropertyChanged嵌套(子)对象的事件.示例代码:

public class Person : INotifyPropertyChanged {

  private string _firstName;
  private int _age;
  private Person _bestFriend;

  public string FirstName {
    get { return _firstName; }
    set {
      // Short implementation for simplicity reasons
      _firstName = value;
      RaisePropertyChanged("FirstName");
    }
  }

  public int Age {
    get { return _age; }
    set {
      // Short implementation for simplicity reasons
      _age = value;
      RaisePropertyChanged("Age");
    }
  }

  public Person BestFriend {
    get { return _bestFriend; }
    set {
      // - Unsubscribe from _bestFriend's INotifyPropertyChanged Event
      //   if not null

      _bestFriend = value;
      RaisePropertyChanged("BestFriend");

      // - Subscribe to _bestFriend's INotifyPropertyChanged Event if not null
      // - When _bestFriend's INotifyPropertyChanged Event is fired, i'd like
      //   to have the RaisePropertyChanged("BestFriend") method invoked
      // - Also, I guess some kind of *weak* event handler is required
      //   if a Person instance i beeing destroyed
    }
  }

  // **INotifyPropertyChanged implementation**
  // Implementation of RaisePropertyChanged method

}
Run Code Online (Sandbox Code Playgroud)

关注BestFriend物业及其价值制定者.现在我知道我可以手动执行此操作,实现注释中描述的所有步骤.但这将是很多代码,特别是当我计划有许多这样的子属性实现时INotifyPropertyChanged.当然它们不会总是相同的类型,它们唯一的共同点就是INotifyPropertyChanged界面.

原因是,在我的实际场景中,我有一个复杂的"Item"(在购物车中)对象,它具有多个层的嵌套对象属性(Item有一个"License"对象,它本身可以再次有子对象)和我需要获得有关"项目"的任何单一更改的通知,以便能够重新计算价格.

你有什么好的建议甚至是一些实施来帮助我解决这个问题吗?

不幸的是,我无法/允许使用像PostSharp这样的后期构建步骤来实现我的目标.

非常感谢你提前,
- 托马斯

thm*_*shd 22

由于我无法找到一个现成的解决方案,我已经完成了基于Pieters(和Marks)建议的自定义实现(谢谢!).

使用这些类,您将收到有关深层对象树中任何更改的通知,这适用于任何INotifyPropertyChanged实现类型和INotifyCollectionChanged*实现集合(显然,我正在使用ObservableCollection它).

我希望这是一个非常干净和优雅的解决方案,它虽然没有经过充分测试,但仍有增强空间.它很容易使用,只需创建一个ChangeListener使用它的静态Create方法的实例并传递你的INotifyPropertyChanged:

var listener = ChangeListener.Create(myViewModel);
listener.PropertyChanged += 
    new PropertyChangedEventHandler(listener_PropertyChanged);
Run Code Online (Sandbox Code Playgroud)

PropertyChangedEventArgs提供PropertyName这将是永远的对象的完整"路径".例如,如果您更改了Persons的"BestFriend"名称,PropertyName则将为"BestFriend.Name",如果其中BestFriend包含Children的集合并且您更改它的Age,则该值将为"BestFriend.Children [].Age",因此上.不要忘记Dispose当你的对象被销毁时,它会(希望)完全取消订阅所有事件监听器.

它在.NET(测试4)和Silverlight(测试4)中编译.因为代码分为三个类,我已经将代码发布到gist 705450,你可以全部抓住它:https://gist.github.com/705450**

*)代码工作的一个原因是它ObservableCollection也实现了INotifyPropertyChanged,否则它不会按预期工作,这是一个众所周知的警告

**)免费使用,根据麻省理工学院许可证发布


Pie*_*kel 17

我认为你正在寻找的东西就像WPF绑定.

如果INotifyPropertyChanged工作是RaisePropertyChanged("BestFriend");必须的,只有在财产BestFriend变化时才能进行.当对象本身发生任何变化时.

如何实现这一点是通过一个两步INotifyPropertyChanged事件处理程序.您的听众将注册已更改的事件Person.当BestFriend被设置/改变,你的改变的情况下注册BestFriend Person.然后,您开始侦听该对象的已更改事件.

这正是WPF绑定实现这一点的方式.通过该系统监听嵌套对象的更改.

当你实现它时,它不起作用的原因是Person级别可以变得非常深,并且改变的事件BestFriend不再意味着什么("改变了什么?").当你有循环关系时,这个问题变得更大,例如你的最好的朋友是你最好的恶魔的母亲.然后,当其中一个属性发生更改时,会出现堆栈溢出.

那么,如何解决这个问题就是创建一个可以构建监听器的类.例如,您可以构建一个侦听器BestFriend.FirstName.然后,该类将在更改的事件上放置一个事件处理程序Person并监听更改BestFriend.然后,当它发生变化时,它会调出一个监听器BestFriend并监听其变化FirstName.然后,当它发生变化时,它会发送一个事件然后你就可以听了.这基本上是WPF绑定的工作原理.

有关WPF绑定的更多信息,请参见http://msdn.microsoft.com/en-us/library/ms750413.aspx.