Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MVVM: Modified model, how to correctly update ViewModel and View?

Case

Say I have a Person class, a PersonViewModel and a PersonView.

Updating properties from PersonView to the Person model is simple enough. PersonViewModel contains a Person object and has public properties the PersonView binds to in order to update the Person model.

However.

Imagine the Person model can get updated by Service. Now the property change needs to be communicated to the PersonViewModel and then to the PersonView.

This is how I would fix it:

For each property on the Person model I would raise the PropertyChanged event. PersonViewModel subscribes to the PropertyChanged event of Person. PersonViewModel would then raise another PropertyChanged in order to update the PersonView.

This to me seems the most obvious way but I kind of want to throw this question out there in the hope of someone showing me a nicer way. Is it really this simple or are there better ways to mark the model as modified and update the respective properties on the ViewModel?

Additions

The PersonView's DataContext is PersonViewModel. Person gets populated from JSON and gets updated many times during its lifetime.

Feel free to suggest architectual changes for my particular case.

Answer

I marked aqwert as the answer of my question since it provided me with an alternative to the solution I already proposed.

like image 735
ndsc Avatar asked Apr 25 '12 21:04

ndsc


People also ask

How does ViewModel update the view?

In a typical MVVM application, you use bindings to connect the view to the ViewModel. Bindings are automatically updated when the ViewModel raises the PropertyChanged event, defined by the INotifyPropertyChanged interface.

How the model changes could be notified to ViewModel in MVVM?

If you want your Models to alert the ViewModels of changes, they should implement INotifyPropertyChanged, and the ViewModels should subscribe to receive PropertyChange notifications. But typically this is only needed if more than one object will be making changes to the Model's data, which is not usually the case.

What goes in the model in MVVM?

Using the MVVM pattern, the UI of the app and the underlying presentation and business logic is separated into three separate classes: the view, which encapsulates the UI and UI logic; the view model, which encapsulates presentation logic and state; and the model, which encapsulates the app's business logic and data.


2 Answers

When the view binds directly to the model (which is also the case when the ViewModel exposes the Model) you are mixing UI code and data code. The goal of MVVM is to separate these two code domains. That's what the ViewModel is for.

The view model has to have it's own properties the view can bind to. An example:

class PersonViewModel {     private Person OriginalModel { get; set; }      public ValueViewModel<string> Name { get; set; }     public ValueViewModel<int> Postcode { get; set; }      protected void ReadFromModel(Person person)     {         OriginalModel = person;         Name.Value = OriginalModel.Name;         Postcode.Value = OriginalModel.Postcode;     }      protected Person WriteToModel()     {         OriginalModel.Name = Name.Value; //...         return OriginalModel;     } } 

Using such a ViewModel-design really separates your data objects from your user interface code. When the structure of the class Person is changed, the UI doesn't need to be fit accordingly, because the ViewModel separates them from each other.

Now to your question. As you can see in the example above, I used a generic ValueViewModel<T>. This class implements INotifyPropertyChanged (and some other stuff). When you receive a new Person instance, you only have to call ReadFromModel(newPerson) on your ViewModel to have the UI updated, because the ValueViewModels the View binds to will inform the UI when their value changes.

Here an extremely simplified example of the internal structure of the ValueViewModel:

class ValueViewModel<T> : INotifyPropertyChanged {     private T _value;     public T Value      {         get { return _value;}         set         {             _value = value;             RaisePropertyChanged("Value");         }     } } 

This is an approach we used in our MVVM library. It has the advantage that it forces the developer to clearly separate code from the designers concerns. And, as a side effect, it generates a standardized code layout in all your Views and ViewModels and thus improves code quality.

like image 179
PVitt Avatar answered Sep 26 '22 16:09

PVitt


If the view is binding to the Model directly then as long as the service is using the same instance any changes to the model properties will be propogated to the view.

However if you are recreating a new model in the service then you will give the viewmodel the new model. I expect to see the model as a property on the view model so when you set that property all binding should be alerted to the change.

//in the ViewModel public Person Model {    get { return _person; }    set { _person = value;           RaisePropertyChanged("Model");  //<- this should tell the view to update         } } 

EDIT:

Since you state there are specific ViewModel logic then you can tailor those properties in the ViewModel

 private void Model_PropertyChanged(object sender, PropertyChangedEventArgs e)  {       if(e.PropertyName == "Prop1") RaisePropertyChanged("SpecicalProperty");       ...  }    public string SpecicalProperty   {      get      {          reutrn Model.Prop1 + " some additional logic for the view";       }    } 

IN XAML

  <TextBlock Text="{Binding Model.PropertyDirect}" />     <TextBlock Text="{Binding SpecicalProperty}" /> 

This way only both the Model and ViewModel propertys are bound to the view without duplicating the data.

You can get fancier creating a helper to link the property changes from the model to the view model or use a mapping dictionary

 _mapping.Add("Prop1", new string[] { "SpecicalProperty", "SpecicalProperty2" }); 

and then find the properties to update by getting the list of properties

 private void Model_PropertyChanged(object sender, PropertyChangedEventArgs e)  {        string[] props;       if(_mapping.TryGetValue(e.PropertyName, out props))       {           foreach(var prop in props)               RaisePropertyChanged(prop);       }   } 
like image 31
aqwert Avatar answered Sep 24 '22 16:09

aqwert