订阅嵌套(子)对象的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 }
专注于BestFriend
Property及其价值BestFriend
者。 现在我知道我可以手动执行此操作 ,实现注释中描述的所有步骤。 但这将是很多代码,特别是当我计划有许多子属性实现这样的INotifyPropertyChanged
。 当然它们不会总是相同的类型,它们唯一的共同点是INotifyPropertyChanged
接口。
原因是,在我的真实场景中,我有一个复杂的“Item”(在购物车中)对象,它在多个层上具有嵌套对象属性(Item有一个“License”对象,它本身可以再次有子对象)和我需要获得有关“项目”的任何单一更改的通知,以便能够重新计算价格。
你有什么好的建议甚至是一些实施来帮助我解决这个问题吗?
不幸的是,我无法/允许使用像PostSharp这样的后期构建步骤来实现我的目标。
非常感谢你提前,
– 托马斯
由于我无法找到现成的解决方案,我已经完成了基于Pieters(和Marks)建议的自定义实现(谢谢!)。
使用这些类,您将收到有关深层对象树中任何更改的通知,这适用于任何实现类型的INotifyPropertyChanged
和实现集合的INotifyCollectionChanged
*(显然,我正在使用ObservableCollection
)。
我希望这是一个非常干净和优雅的解决方案,它虽然没有经过充分测试,但仍有增强空间。 它非常容易使用,只需使用它的静态Create
方法创建一个ChangeListener
实例并传递INotifyPropertyChanged
:
var listener = ChangeListener.Create(myViewModel); listener.PropertyChanged += new PropertyChangedEventHandler(listener_PropertyChanged);
PropertyChangedEventArgs
提供了一个PropertyName
,它始终是对象的完整“路径”。 例如,如果您更改人员的“BestFriend”名称,则PropertyName
将为“BestFriend.Name”,如果BestFriend
具有BestFriend
并且您将其更改为Age,则该值将为“BestFriend.Children []。Age”等等。 不要忘记在对象被销毁时Dispose
,然后它(希望)完全取消订阅所有事件监听器。
它在.NET(测试4)和Silverlight(测试4)中编译。 因为代码分为三个类,我已将代码发布到gist 705450 ,您可以在其中全部获取: https : //gist.github.com/705450 **
*)代码工作的一个原因是ObservableCollection
也实现了INotifyPropertyChanged
,否则它将无法正常工作,这是一个众所周知的警告
**)免费使用,根据MIT许可证发布
我认为你正在寻找的东西就像WPF绑定。
INotifyPropertyChanged
如何工作是RaisePropertyChanged("BestFriend");
只有在属性BestFriend
更改时BestFriend
。 当对象本身的任何内容发生变化时。
如何实现这一点是通过两步INotifyPropertyChanged
事件处理程序。 您的听众将注册更改的Person
事件。 设置/更改BestFriend
,您可以注册BestFriend
Person
的已更改事件。 然后,您开始侦听该对象的已更改事件。
这正是WPF绑定实现这一点的方式。 通过该系统监听嵌套对象的更改。
当你在Person
实现它时,它不起作用的原因是级别可以变得非常深,而且BestFriend
的更改事件不再具有任何意义(“已经改变了什么?”)。 当你有循环关系时,这个问题变得更大,例如你的最好的朋友是你最好的恶魔的母亲。 然后,当其中一个属性发生更改时,会出现堆栈溢出。
那么,如何解决这个问题就是创建一个可以构建监听器的类。 例如,您可以在BestFriend.FirstName
上构建一个侦听BestFriend.FirstName
。 然后,该类将在已更改的Person
事件上放置一个事件处理程序,并在BestFriend
上侦听更改。 然后,当它发生变化时,它会在BestFriend
上放置一个监听BestFriend
并监听FirstName
变化。 然后,当它发生变化时,它会发送一个事件然后你就可以听了。 这基本上是WPF绑定的工作原理。
有关WPF绑定的更多信息,请参见http://msdn.microsoft.com/en-us/library/ms750413.aspx 。
有趣的解决方案Thomas。
我发现了另一个解决方 它被称为传播者设计模式。 您可以在网上找到更多信息(例如,在CodeProject: C#中的Propagator – 观察者设计模式的替代品 )。
基本上,它是用于更新依赖关系网络中的对象的模式。 当需要通过对象网络推送状态更改时,它非常有用。 状态变化由物体本身表示,物体通过传播者网络传播。 通过将状态更改封装为对象,传播器变得松散耦合。
可重用的Propagator类的类图:
阅读CodeProject的更多信息。
我写了一个简单的帮手来做到这一点。 您只需在父视图模型中调用BubblePropertyChanged(x => x.BestFriend)。 nb假设您的父级中有一个名为NotifyPropertyChagned的方法,但您可以对其进行调整。
/// /// Bubbles up property changed events from a child viewmodel that implements {INotifyPropertyChanged} to the parent keeping /// the naming hierarchy in place. /// This is useful for nested view models. /// /// Child property that is a viewmodel implementing INotifyPropertyChanged. /// public IDisposable BubblePropertyChanged(Expression> property) { // This step is relatively expensive but only called once during setup. MemberExpression body = (MemberExpression)property.Body; var prefix = body.Member.Name + "."; INotifyPropertyChanged child = property.Compile().Invoke(); PropertyChangedEventHandler handler = (sender, e) => { this.NotifyPropertyChanged(prefix + e.PropertyName); }; child.PropertyChanged += handler; return Disposable.Create(() => { child.PropertyChanged -= handler; }); }
在CodeProject上查看我的解决方案: http : //www.codeproject.com/Articles/775831/INotifyPropertyChanged-propagator它完全符合您的需要 – 在此嵌套或任何嵌套中的相关依赖项时,有助于传播(以优雅方式)依赖属性查看模型更改:
public decimal ExchTotalPrice { get { RaiseMeWhen(this, has => has.Changed(_ => _.TotalPrice)); RaiseMeWhen(ExchangeRate, has => has.Changed(_ => _.Rate)); return TotalPrice * ExchangeRate.Rate; } }
我已经在网上搜索了一天,我找到了另一个来自Sacha Barber的好解决方案:
http://www.codeproject.com/Articles/166530/A-Chained-Property-Observer
他在Chained Property Observer中创建了弱引用。 如果您想看到另一种实现此function的好方法,请查看文章。
我还想提及Reactive Extensions的一个很好的实现@ http://www.rowanbeach.com/rowan-beach-blog/a-system-reactive-property-change-observer/
此解决方案仅适用于一个观察者级别,而不是完整的观察者链。