MVVM和INotifyPropertyChanged问题

我有一个MVVM设计的大问题。 我试图捕获我的内部嵌套对象的每个PropertyChanged,包括他们的嵌套对象的更多属性,在我的ViewModel中,但我不知道如何做到这一点。

这是我的结构:

class MyVM { public MyVM() { this.SomeData = new SomeData(); this.SomeData.NestedObj = new MyNestedDat(); this.SomeData.Str = "This tiggers propertychanged inside MyDat class"; // this triggers propertychanged event inside MyNestedDat class this.SomeData.NestedObj.Num = 123; } // and here should be a method where i catch all possibe propertychanges from my nested objets and their nested objets, how do i do that? public MyDat SomeData { get; set; } } class MyDat : INotifyPropertyChanged { private string str; public string Str; { get { return this.str;} set { this.str = value; this.PropertyChanged(this, "Str"); } } publicMyNestedDat NestedObj { get; set; } } class MyNestedDat : INotifyPropertyChanged { private int num; public int Num { get{ return this.num;} set { this.num = value; this.PropertyChanged(this, "Num"); } } } 

我如何让它工作? 我真的很无知从哪里开始。

MyNestedDat类抛出PropertyChanged,MyDat类抛出propertychanged,我想在我的viewmodel中捕获它们。 我怎样才能做到这一点?

在我看来,你提出的问题有一些概念上的错误。 想象一下,你得到的解决方案适合你的场景(你很满意)并考虑以下内容:

  • 如果添加另一个图层会发生什么? 你还期望它的工作方式一样吗?
  • 是否应传播属性更改( viewModel1.propA通知viewModel2.PropA )?
  • 是否应该转换属性更改( viewModel1.SomeProp通知ViewModel2.AnotherProp )?
  • 性能是一个问题吗? 如果您需要通过多个级别传播属性更改事件,这将如何执行?

这应该引起警惕,目前的方法不是正确的道路。

你需要的是一种以松散耦合的方式在viewModel之间提供通信的方法,这样你的viewModels甚至不需要知道彼此的存在。 这样做的好处在于,这也适用于其他情况,而不仅仅是财产变化。

对于属性更改事件的情况,一个viewModel想要知道什么时候发生 (它可能是属性更改事件以外的事情,请记住)。 这意味着另一个viewModel需要某种方式来说“嘿,属性已经改变”(或“我的状态已经改变”,“数据库调用已经完成”等)。

现在在C#中你可以提供提供此function的事件….除了,现在你的对象知道了彼此,这给你留下了你以前遇到的同样问题。

要解决这个问题,你需要另一个对象,一个中介(在这个例子中称之为Messenger ),其唯一目的是处理在对象之间传递的消息,这样他们就可以生活在彼此的无知之中。

一般的想法是这样的。 在提供通知的viewModel中,您可能会执行以下操作:

 public string MyProp { get { return _myProp; } set { _mProp = value; OnPropertyChanged("MyProp"); Messenger.PostMessage(new VMChangedMessage { ViewModel = this, PropertyName = "MyProp" }); } } 

在对事件感兴趣的viewModel中,您可能会执行以下操作:

 public class ViewModel2 { public ViewModel2() { Messenger.Subscribe(handleMessage); } private void handleMessage(VMChangedMessage msg) { // Do something with the information here... } } 

请注意,两个viewModels永远不会互相引用。 他们现在松散耦合。

有许多预先存在的实现已经可用,并且创建自己的实现并不困难(信使基本上保留对特定消息感兴趣的对象列表,并在需要通知感兴趣的各方时迭代列表) 。 有些事情可以用不同的方式实现(有些实现只是传递字符串消息而不是将信息封装在对象中,有些实现自动清理观察者)。

我建议使用包含信使类的Josh Smiths(优秀) MVVM Foundation 。 它也是开源的,所以你可以看到它是如何工作的。

PropertyNamePropertyName应包含的内容没有明确的约束。

有关嵌套(子)对象,请参阅订阅INotifyPropertyChanged 。

这是一个例子:

 class A : BaseObjectImplementingINotifyPropertyChanged { private string m_name; public string Name { get { return m_name; } set { if(m_name != value) { m_name = value; RaisePropertyChanged("Name"); } } } } class B : BaseObjectImplementingINotifyPropertyChanged { private A m_a; public AA { get { return m_a; } set { if(m_a != value) { if(m_a != null) m_a.PropertyChanged -= OnAPropertyChanged; m_a = value; if(m_a != null) m_a.PropertyChanged += OnAPropertyChanged; RaisePropertyChanged("A"); } } } private void OnAPropertyChanged(object sender, PropertyChangedEventArgs e) { RaisePropertyChanged("A." + e.PropertyName); } } B b = new B(); b.PropertyChanged += (s, e) => { Console.WriteLine(e.PropertyName); }; bAName = "Blah"; // Will print "A.Name" 

这里最好的做法是分离ModelViewModel的想法。

通过使ViewModel对象比Model更平坦,您可以避免这种情况。 使用Automapper等自动映射工具,您可以将Model映射到ViewModel ,反之亦然。

https://github.com/AutoMapper/AutoMapper/wiki/Flattening

 class MyDatViewModel : INotifyPropertyChanged { public string Str { // ... Get Set } public int NestedObjNum { // ... Get set } } // Configure AutoMapper Mapper.CreateMap(); // Perform mapping MyDatViewModel viewModel = Mapper.Map(someData);