MVVM and INotifyPropertyChanged Issue

前端 未结 3 408
我寻月下人不归
我寻月下人不归 2021-01-15 04:50

I have a big problem with MVVM design. I am trying to catch every PropertyChanged of my inner nested objects, including futhermore propertchanged of their nested objects, in

相关标签:
3条回答
  • 2021-01-15 05:28

    In my opinion there are a few conceptual things wrong with what you are asking. Just imagine you get a solution that works for your scenario (that you are happy with) and consider the following:

    • What happens if another layer is added? do you still expect it to work the same?
    • Should property changes be propagated (viewModel1.propA notifies viewModel2.PropA)?
    • Should property changes be transformed (viewModel1.SomeProp notifies ViewModel2.AnotherProp)?
    • Is performance a concern? how will this perform if you need to propagate the property changed events through many levels?

    This should be raising alarm bells that the current approach is not the right path to tread.

    What you need is a way to provide communication between your viewModels in a loosely coupled way so that you viewModels do not even need to know about each others existence. The beauty of this is that this will also work in other situations not just for property changes.

    For your case of property changed events, one viewModel wants to know when something happens (it could be something other than a property changed event, remember). This means the other viewModel needs some way of saying "Hey, a property has changed" (or "My state has changed", "That database call has finished" etc).

    Now in C# you can provide events which provide this feature....except, now your objects know about each other which leaves you with the same problem you had before.

    To overcome this problem you need another object, a mediator (lets call it Messenger in this example), whose sole purpose is to handle the message passing between the objects so that they can live in ignorance of each other.

    The general idea is this. In the viewModel that provides notifications you might do something like this:

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

    And in the viewModel that is interested in the event you might do something like this:

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

    Notice that the two viewModels never reference each other. They are now loosely-coupled.

    There are a number of pre-existing implementations already available and it isn't difficult to create your own (the messenger basically keeps a list of objects that are interested in a certain message and iterates the list when it needs to notify the interested parties). There are a few things that can be implemented differently (some implementations just pass string messages around rather than encapsulating the information in objects, and some handle the clean-up of observers automatically).

    I would recommend using Josh Smiths (excellent) MVVM Foundation which includes a messenger class. It's also open source so you can see how it works.

    0 讨论(0)
  • 2021-01-15 05:28

    The best thing to do here is to separate the idea of a Model and a ViewModel.

    By having a ViewModel object that is flatter than the Model you can avoid this scenario. Using an automatic mapping tool like Automapper then allows you to map the Model to the ViewModel and vice versa.

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

    class MyDatViewModel : INotifyPropertyChanged
    {
        public string Str
        {
            // ... Get Set
        }
    
        public int NestedObjNum
        {
            // ... Get set
        }
    }
    
    // Configure AutoMapper
    
    Mapper.CreateMap<MyDat, MyDatViewModel>();
    
    // Perform mapping
    
    MyDatViewModel viewModel = Mapper.Map<MyDat, MyDatViewModel>(someData);
    
    0 讨论(0)
  • 2021-01-15 05:41

    There is no clear constraint about what PropertyName should contains in PropertyChangedEventArgs.

    See Subscribe to INotifyPropertyChanged for nested (child) objects.

    Here is an example :

    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 A A {
        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); };
    b.A.Name = "Blah"; // Will print "A.Name"
    
    0 讨论(0)
提交回复
热议问题