MVVM in WPF - How to alert ViewModel of changes in Model... or should I?

Fairly old thread but after a lot of searching I came up with my own solution: A PropertyChangedProxy

With this class, you can easily register to someone else's NotifyPropertyChanged and take appropriate actions if it is fired for the registered property.

Here's a sample of how this could look like when you have a model property "Status" which can change on it's own and then should automatically notify the ViewModel to fire it's own PropertyChanged on it's "Status" property so that the view is also notified :)

public class MyModel : INotifyPropertyChanged
{
    private string _status;
    public string Status
    {
        get { return _status; }
        set { _status = value; OnPropertyChanged(); }
    }

    // Default INotifyPropertyChanged
    public event PropertyChangedEventHandler PropertyChanged;
    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        var handler = PropertyChanged;
        if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
    }
}

public class MyViewModel : INotifyPropertyChanged
{
    public string Status
    {
        get { return _model.Status; }
    }

    private PropertyChangedProxy<MyModel, string> _statusPropertyChangedProxy;
    private MyModel _model;
    public MyViewModel(MyModel model)
    {
        _model = model;
        _statusPropertyChangedProxy = new PropertyChangedProxy<MyModel, string>(
            _model, myModel => myModel.Status, s => OnPropertyChanged("Status")
        );
    }

    // Default INotifyPropertyChanged
    public event PropertyChangedEventHandler PropertyChanged;
    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        var handler = PropertyChanged;
        if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
    }
}

and here's the class itself:

/// <summary>
/// Proxy class to easily take actions when a specific property in the "source" changed
/// </summary>
/// Last updated: 20.01.2015
/// <typeparam name="TSource">Type of the source</typeparam>
/// <typeparam name="TPropType">Type of the property</typeparam>
public class PropertyChangedProxy<TSource, TPropType> where TSource : INotifyPropertyChanged
{
    private readonly Func<TSource, TPropType> _getValueFunc;
    private readonly TSource _source;
    private readonly Action<TPropType> _onPropertyChanged;
    private readonly string _modelPropertyname;

    /// <summary>
    /// Constructor for a property changed proxy
    /// </summary>
    /// <param name="source">The source object to listen for property changes</param>
    /// <param name="selectorExpression">Expression to the property of the source</param>
    /// <param name="onPropertyChanged">Action to take when a property changed was fired</param>
    public PropertyChangedProxy(TSource source, Expression<Func<TSource, TPropType>> selectorExpression, Action<TPropType> onPropertyChanged)
    {
        _source = source;
        _onPropertyChanged = onPropertyChanged;
        // Property "getter" to get the value
        _getValueFunc = selectorExpression.Compile();
        // Name of the property
        var body = (MemberExpression)selectorExpression.Body;
        _modelPropertyname = body.Member.Name;
        // Changed event
        _source.PropertyChanged += SourcePropertyChanged;
    }

    private void SourcePropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        if (e.PropertyName == _modelPropertyname)
        {
            _onPropertyChanged(_getValueFunc(_source));
        }
    }
}

Short answer: it depends on the specifics.

In your example the models are being updated "on their own" and these changes of course need to somehow propagate to the views. Since the views can only directly access the viewmodels, it means the model must communicate these changes to the corresponding viewmodel. The established mechanism for doing so is of course INotifyPropertyChanged, which means that you 'll get a workflow like this:

  1. Viewmodel is created and wraps model
  2. Viewmodel subscribes to model's PropertyChanged event
  3. Viewmodel is set as view's DataContext, properties are bound etc
  4. View triggers action on viewmodel
  5. Viewmodel calls method on model
  6. Model updates itself
  7. Viewmodel handles model's PropertyChanged and raises its own PropertyChanged in response
  8. View reflects the changes in its bindings, closing the feedback loop

On the other hand if your models contained little (or no) business logic, or if for some other reason (such as gaining transactional capability) you decided to let each viewmodel "own" its wrapped model then all modifications to the model would pass through the viewmodel so such an arrangement would not be necessary.

I describe such a design in another MVVM question here.


Your choices:

  • Implement INotifyPropertyChanged
  • Events
  • POCO with Proxy manipulator

As I see it, INotifyPropertyChanged is a fundamental part of .Net. i.e. its in System.dll. Implementing it in your "Model" is akin to implementing an event structure.

If you want pure POCO, then you effectively have to manipulate your objects via proxies/services and then your ViewModel is notified of changes by listening to the proxy.

Personally I just loosely implement INotifyPropertyChanged and then use FODY to do the dirty work for me. It looks and feels POCO.

An example (using FODY to IL Weave the PropertyChanged raisers):

public class NearlyPOCO: INotifyPropertyChanged
{
     public string ValueA {get;set;}
     public string ValueB {get;set;}

     public event PropertyChangedEventHandler PropertyChanged;
}

then you can have your ViewModel listen to PropertyChanged for any changes; or property specific changes.

The beauty of the INotifyPropertyChanged route, is you chain it up with an Extended ObservableCollection. So you dump your near poco objects into a collection, and listen to the collection... if anything changes, anywhere, you learn about it.

I'll be honest, this could join the "Why wasn't INotifyPropertyChanged autmatically handled by the compiler" discussion, which devolves to: Every object in c# should have the facility to notify if any part of it was changed; i.e. implement INotifyPropertyChanged by default. But it doesn't and the best route, that requires the least amount of effort, is to use IL Weaving (specifically FODY).


If you want your Models to alert the ViewModels of changes, they should implement INotifyPropertyChanged, and the ViewModels should subscribe to receive PropertyChange notifications.

Your code might look something like this:

// Attach EventHandler
PlayerModel.PropertyChanged += PlayerModel_PropertyChanged;

...

// When property gets changed in the Model, raise the PropertyChanged 
// event of the ViewModel copy of the property
PlayerModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
    if (e.PropertyName == "SomeProperty")
        RaisePropertyChanged("ViewModelCopyOfSomeProperty");
}

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.

If you ever have a case where you don't actually have a reference to your Model property to attach the PropertyChanged event to it, then you can use a Messaging system such as Prism's EventAggregator or MVVM Light's Messenger.

I have a brief overview of messaging systems on my blog, however to summarize it, any object can broadcast a message, and any object can subscribe to listen for specific messages. So you might broadcast a PlayerScoreHasChangedMessage from one object, and another object can subscribe to listen for those types of messages and update it's PlayerScore property when it hears one.

But I don't think this is needed for the system you have described.

In an ideal MVVM world, your application is comprised of your ViewModels, and your Models are the just the blocks used to build your application. They typically only contain data, so would not have methods such as DrawCard() (that would be in a ViewModel)

So you would probably have plain Model data objects like these:

class CardModel
{
    int Score;
    SuitEnum Suit;
    CardEnum CardValue;
}

class PlayerModel 
{
    ObservableCollection<Card> FaceUpCards;
    ObservableCollection<Card> FaceDownCards;
    int CurrentScore;

    bool IsBust
    {
        get
        {
            return Score > 21;
        }
    }
}

and you'd have a ViewModel object like

public class GameViewModel
{
    ObservableCollection<CardModel> Deck;
    PlayerModel Dealer;
    PlayerModel Player;

    ICommand DrawCardCommand;

    void DrawCard(Player currentPlayer)
    {
        var nextCard = Deck.First();
        currentPlayer.FaceUpCards.Add(nextCard);

        if (currentPlayer.IsBust)
            // Process next player turn

        Deck.Remove(nextCard);
    }
}

(Above objects should all implement INotifyPropertyChanged, but I left it out for simplicity)

Tags:

C#

.Net

Wpf

Mvvm