39

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.

ndsc
  • 1,173
  • 2
  • 13
  • 22

2 Answers2

36

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.

PVitt
  • 11,500
  • 5
  • 51
  • 85
  • 1
    Good explanation and great snippet of the generic `ValueViewModel` class! Please consider implementing equality check for property changed notification. By the way, [here is](https://gist.github.com/sbrunov/6535186) the implementation of `ValueViewModel` class using `ViewModelBase` class of MVVM Light framework. – Sergey Vyacheslavovich Brunov Sep 12 '13 at 09:59
  • Thanks for your advice. In the meantime we already have added an equality check. – PVitt Sep 12 '13 at 10:45
  • @PVitt cloud you please take a look at [this Question](http://stackoverflow.com/questions/17163675/how-to-pass-idataerrorinfo-validation-through-an-wrapper-to-the-xaml) it's related to your answer so maybe you can enlighten me how to solve this. thanks in advance – WiiMaxx Sep 16 '13 at 11:22
  • @WiiMaxx I'm sorry, but I don't really understand the problem you are describing over there. This is for sure because, since over a year now, I'm not a .NET developer anymore and thus my knowledge in this area is fading. – PVitt Sep 16 '13 at 13:01
  • @PVitt my Wrapper is basically the same like like your `ValueViewModel` with the difference that he should also contain Boolean property which will return if the current Value == the first value he get's that part works but the problem is i use [DataAnnotations](http://msdn.microsoft.com/en-us/library/dd901590(v=vs.95).aspx) + [IDataError](http://www.asp.net/mvc/tutorials/older-versions/models-(data)/validating-with-the-idataerrorinfo-interface-cs) to check if the Value is valid but i'm not able to bubble this information through my wrapper – WiiMaxx Sep 16 '13 at 13:20
  • @PVitt although I understand your point, I have seen many examples on internet using RaisePropertyChanged in the model to update the UI. This is what you think is wrong according to the MVVM pattern, right? For example this https://rachel53461.wordpress.com/2011/05/08/simplemvvmexample/ doesn't adhere the pattern. Did I understand well? – gts13 May 16 '17 at 08:27
  • @gts13 Exaclty. The author says "Models are simple class objects that hold data.", but has the model implement the INotifyPropertyChanged interface. That's not the models purpose. That's what the ViewModel is for. – PVitt May 16 '17 at 08:40
  • @PVitt is there out any sample/example that adheres the MVVM pattern like your answer? I have spent a ton of time to find a right way like yours! – gts13 May 16 '17 at 08:43
  • 1
    @gts13 To be honest, I don't know. I left the .NET ecosystem some years ago and thus I don't follow it's development anymore. However, have a look at http://stackoverflow.com/questions/1405739/mvvm-tutorial-from-start-to-finish – PVitt May 16 '17 at 08:49
  • @PVitt just one last question. Since I am using MVVM light in my project. Do you think is a good idea if the Model notifies via the Messenger the ViewModel for a change, and then the ViewModel updates the View? For me it seems the easiest way, and I think it doesn't break the mvvm pattern. – gts13 May 16 '17 at 09:02
  • 1
    @gts13 From my understanding, this is a reversal of responsibilities. The ViewModel is the active part that get's the new Model. How the ViewModel get's the new Model *delivered* is irrelevant. But the Model is just a dumb data object without any code. Just think of a warehouse. The stuff in the warehouse is the Model. The warehouse manager is the ViewModel. The stuff can't tell that it's old. Someone has to tell the warehouse manager that his stuff is old and he needs to get new stuff. – PVitt May 16 '17 at 09:20
8

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);
      } 
 }
aqwert
  • 10,559
  • 2
  • 41
  • 61
  • Sorry, I forgot to mention that the PersonView's DataContext is the ViewModel. The ViewModel contains a lot of UI logic separate from the model. – ndsc Apr 25 '12 at 21:28
  • Yes that is typical. When you expose the model through the view model then changing the instance to another model should update the view – aqwert Apr 25 '12 at 21:30
  • Yes, in my case though only a few properties will be changed during the lifetime of the model. Would you suggest replacing the complete model object instead of looking for a way to update separate properties? I feel this might cause a lot of overhead but this is just a hunch. – ndsc Apr 25 '12 at 21:32
  • If your model object is "heavy", and only a small number of properties change when you deserialize the incoming JSON, it may be worth creating wrapper properties in the view model. Otherwise I'd go with the complete model object replacement as long as no UI performance issues are seen. – Alan Apr 25 '12 at 21:35
  • Is the service updating the same instance of the `Person` model that the view model holds? If so, then any property changes will get updated on the view. If you have specific logic on the view model that expose more properties based on data within the model then you will need to listen for changes from the model. Is this what you are doing? – aqwert Apr 25 '12 at 21:36
  • @Alan It's heavy in the sense when properties change other logic will get fired that might or might not be heavy. This could be handled by the View but I feel that's just pushing the problem more towards the UI. – ndsc Apr 25 '12 at 21:41
  • @aqwert Yes the service is updating the same instance (based on GUID). Yes that's what I am doing. The solution I propose in my original question is exactly what I need. However, I don't feel it's the best. Somehow feels that the PropertyChanged event being raised twice is overhead. – ndsc Apr 25 '12 at 21:45
  • If you have the view binding to the model directly then the view model does not have to raise anything. The model will raise the event on the property and will be seen by the view. – aqwert Apr 25 '12 at 21:47
  • @alan Why would there be no point in defining wrappers? I would imagine wrappers making more sense if the View Model has to do additional work to process property changes on the model. For example: I got a case where I want to time (using a Timer) changes on the UI and only change the model every X seconds. – ndsc Apr 25 '12 at 21:54
  • @ndsc: Do you have derived properties on the view model (ie when the Person model object changes, do you calculate other convenience properties on the view model for the view)? – Alan Apr 25 '12 at 21:55
  • 1
    @ndsc: Sorry, I deleted my previous comment, as I felt aqwert really nailed it with the last one. Re wrappers, it's absolutely true, that would be another reason to strongly decouple the model and the view. I don't think you should worry about raising the PC event twice, it's kind of the point of having clear separation of concerns, and the overhead of that in itself is negligible. – Alan Apr 25 '12 at 21:56
  • @alan My comment is also valid for aqwert's reply. To answer your question: yes. – ndsc Apr 25 '12 at 21:57
  • @ndsc: Sure. Then wrappers are the way to go; this would also avoid the awkwardness of having to bind to PropertyA, PropertyB as well as Model.PropertyC, Model.PropertyD etc. in the view. At least the difference in binding paths would look arbitrary to me if I had to maintain your code :) – Alan Apr 25 '12 at 22:00
  • Yep you are right with the decoupling and separation of concerns. Having just one model property on the view model seems like a really neat way to do it but in my case I would have to work around that in order to get it to work. I would just be shifting the problem from one place to another. – ndsc Apr 25 '12 at 22:12