代码之家  ›  专栏  ›  技术社区  ›  thmshd

订阅嵌套(子)对象的INotifyPropertyChanged

  •  27
  • thmshd  · 技术社区  · 14 年前

    解决方案来处理 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 属性及其值设置器。现在 ,实现注释中描述的所有步骤。但这将是很多代码,特别是当我计划让许多子属性实现 这样地。当然,他们不会永远是同一类型的,他们唯一的共同点是 更改了InotifyProperty 接口。

    原因是,在我的真实场景中,我有一个复杂的“Item”(在cart中)对象,它在多个层上嵌套了对象属性(Item有一个“License”对象,它本身也可以有子对象),我需要得到关于“Item”的任何单个更改的通知,以便能够重新计算价格。

    你有什么好的建议,甚至是一些 帮助我解决的实现 这个?

    6 回复  |  直到 5 年前
        1
  •  22
  •   thmshd    10 年前

    由于我找不到一个现成的解决方案,所以我做了一个基于Pieters(和Marks)建议的自定义实现(谢谢!).

    使用这些类,将通知您深层对象树中的任何更改,这对任何 INotifyPropertyChanged 实现类型和 INotifyCollectionChanged *实现集合(显然,我使用 ObservableCollection

    我希望这是一个非常干净和优雅的解决方案,它没有充分的测试,但有提高的空间。很容易使用,只需创建 ChangeListener Create 方法并通过 :

    var listener = ChangeListener.Create(myViewModel);
    listener.PropertyChanged += 
        new PropertyChangedEventHandler(listener_PropertyChanged);
    

    这个 PropertyChangedEventArgs PropertyName 这将永远是你的对象的完整“路径”。例如,如果更改某人的“好友”名称,则 属性名 将是“BestFriend.Name”,如果 BestFriend 有一个孩子的集合,你改变它的年龄,值将是“BestFriend.Children[].Age”等等。别忘了 Dispose 当您的对象被销毁时,它将(希望)从所有事件侦听器中完全取消订阅。

    gist 705450 在那里你可以抓住一切: https://gist.github.com/705450 **

    可观测集合 也实施 ,否则就无法按预期工作,这是众所周知的警告

    **)免费使用,发布于 MIT License

        2
  •  17
  •   Pieter van Ginkel    14 年前

    我想你要找的是WPF绑定之类的东西。

    怎么 INotifyPropertyChanged RaisePropertyChanged("BestFriend"); 必须 只有 当财产 BestFriend 变化。当对象本身的任何内容发生变化时就不会。

    如何实现这一点需要两步 更改了InotifyProperty 事件处理程序。您的侦听器将在 Person . 当 获取设置/更改,注册 . 然后,开始侦听该对象的已更改事件。

    当您在中实现它时,这不起作用的原因 是水平会变得很深 不再意味着什么(“什么改变了?”)当你有循环关系时,这个问题会变得更大,比如你的一个月最好的朋友是你最好的朋友的母亲。然后,当其中一个属性更改时,就会出现堆栈溢出。

    所以,如何解决这个问题是创建一个类,用它可以构建监听器。例如,您可以在 BestFriend.FirstName 听上面的变化 最好的朋友 最好的朋友 倾听 FirstName . 然后,当这个改变时,它发送一个引发事件,然后您就可以监听它。这就是WPF绑定的基本原理。

    http://msdn.microsoft.com/en-us/library/ms750413.aspx

        3
  •  3
  •   Marek Takac    13 年前

    有趣的解决方案托马斯。

    我找到了另一个解决办法。这叫做传播器设计模式。您可以在web上找到更多信息(例如,在CodeProject: Propagator in C# - An Alternative to the Observer Design Pattern

    基本上,这是一种更新依赖关系网络中对象的模式。当需要通过对象网络推送状态更改时,它非常有用。状态变化由一个通过传播者网络传播的对象本身表示。通过将状态更改封装为对象,传播程序变得松散耦合。

    可重用传播程序类的类图:

    A class diagram of the re-usable Propagator classes

    阅读更多内容 CodeProject .

        4
  •  0
  •   DanH    13 年前

    我写了一个简单的助手来做这件事。您只需在父视图模型中调用BubblePropertyChanged(x=>x.BestFriend)。n、 有一个假设,你在你的父母身上有一个名为NotifyPropertyChagned的方法,但是你可以修改它。

            /// <summary>
        /// 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. 
        /// </summary>
        /// <param name="property">Child property that is a viewmodel implementing INotifyPropertyChanged.</param>
        /// <returns></returns>
        public IDisposable BubblePropertyChanged(Expression<Func<INotifyPropertyChanged>> 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; });
        }
    
        5
  •  0
  •   KolA    11 年前

    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;
        }
    }
    
        6
  •  0
  •   gReX    9 年前

    我在网上搜索了一天,发现了另一个来自Sacha Barber的不错的解决方案:

    http://www.codeproject.com/Articles/166530/A-Chained-Property-Observer

    他在一个链式属性观察者中创建了弱引用。如果您想看到实现此功能的另一个好方法,请查看文章。

    我还想提到一个很好的实现,它使用了反应式扩展@ http://www.rowanbeach.com/rowan-beach-blog/a-system-reactive-property-change-observer/