C# – MVVM in WPF – How to alert ViewModel of changes in Model… or should I

cmvvmnetwpf

I am going through some MVVM articles, primarily this and this.

My specific question is: How do I communicate Model changes from the Model to the ViewModel?

In Josh's article, I don't see that he does this. The ViewModel always asks the Model for properties. In Rachel's example, she does have the model implement INotifyPropertyChanged, and raises events from the model, but they are for consumption by the view itself (see her article/code for more detail on why she does this).

Nowhere do I see examples where the model alerts the ViewModel of changes to model properties. This has me worried that perhaps it's not done for some reason. Is there a pattern for alerting the ViewModel of changes in the Model? It would seem to be necessary as (1) conceivably there are more than 1 ViewModel for each model, and (2) even if there is just one ViewModel, some action on the model might result in other properties being changed.

I suspect that there might be answers/comments of the form "Why would you want to do that?" comments, so here's a description of my program. I'm new to MVVM so perhaps my whole design is faulty. I'll briefly describe it.

I am programming up something that is more interesting (at least, to me!) than "Customer" or "Product" classes. I am programming BlackJack.

I have a View that doesn't have any code behind and just relies on binding to properties and commands in the ViewModel (see Josh Smith's article).

For better or worse, I took the attitude that the Model should contain not just classes such as PlayingCard, Deck, but also the BlackJackGame class that keeps state of the whole game, and knows when the player has gone bust, the dealer has to draw cards, and what the player and dealer current score is (less than 21, 21, bust, etc.).

From BlackJackGame I expose methods like "DrawCard" and it occurred to me that when a card is drawn, properties such as CardScore, and IsBust should be updated and these new values communicated to the ViewModel. Perhaps that's faulty thinking?

One could take the attitude that the ViewModel called the DrawCard() method so he should know to ask for an updated score and find out if he is bust or not. Opinions?

In my ViewModel, I have the logic to grab an actual image of a playing card (based on suit,rank) and make it available for the view. The model should not be concerned with this (perhaps other ViewModel would just use numbers instead of playing card images). Of course, perhaps some will tell me that the Model should not even have the concept of a BlackJack game and that should be handled in the ViewModel?

Best Answer

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)