Mvvm in Wpf - How to Alert Viewmodel of Changes in Model... or Should I

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

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)

WPF, C#, MVVM Notify ViewModel dynamically in changes from static vars in Model

It appears like you want to notify when a static property changes. For this, you may use the StaticPropertyChanged event.

class ApplyKMM
{
#region Properties
static BitmapImage _Filepath;
public static BitmapImage Filepath
{
get { return _Filepath; }
set { if (_Filepath != value) { _Filepath = value; NotifyPropertyChanged(nameof(Filepath)); } }
}
#endregion

#region Static NotifyPropertyChanged
public static void NotifyStaticPropertyChanged(string propertyName)
{
StaticPropertyChanged?.Invoke(null, new PropertyChangedEventArgs(propertyName));
}

public void NotifyAllStaticPropertyChanged()
{
NotifyStaticPropertyChanged(string.Empty);
}

public static event EventHandler<PropertyChangedEventArgs> StaticPropertyChanged;
#endregion
}

Please take note that this is available from WPF Version 4.5.

You might also find this question interesting.

How to notify a view in MVVM when the model is changed but no properties changed

So, in your view model you have an ObservableCollection of items (say ObservableCollection). What you need to do is to make sure that MyItem (that is, each item in the collection) derives from INotifyPropertyChanged. Then, when you change the items within the collection, fire the PropertyChanged event. The WPF List View will pick it up.

The RaisePropertyChanged described by other commentors is a utility method typically added to VM classes (often, to a common base class of all VM classes). It raises the PropertyChanged event:

protected void RaisePropertyChanged( string prop ) {
if( PropertyChanged != null ) {
PropertyChanged( this, new PropertyChangedEventArgs(prop) );
}
}

As described by other commentors, there is nothing that dictate you raise a PropertyChanged event only from within the setter. Within your Copy method, just call the RaisePropertyChanged with the appropriate property names (you can call it multiple times, for each property that has changed).

EDIT: For the status line, if you want to follow the MVVM design pattern, you shouldn't call methods from the VM to the View (that's a key concept in MVVM: The ViewModel is View agnostic). You should simply call the RaisePropertyChanged method for all the properties that control the status lines.

Correct way to update property in ViewModel from Model

If the model implements the INotifyPropertyChanged interface you can bind directly to it from the view:

<Button Content="Button" IsEnabled="{Binding Authenticated}" />

Note that the LoginModel class must raise the PropertyChanged event whenever the Authenticated property is set to a new value.

You could also expose the entire model entity through the view model class:

public class ViewModel
{
public ViewModel(LoginModel model)
{
Model = model;
}

public LoginModel Model { get; }
}

...and bind to it like this:

<Button Content="Button" IsEnabled="{Binding Model.Authenticated}" />

It is still the model class that must implement the INotifyPropertyChanged interface and raise change notifications.

Another option is for the view model to wrap any property of the model class that you want to be able to bind to from the view. Then you bind to a property of the view model class that in turn wraps a property of the model class something like this:

public class ViewModel
{
private readonly LoginModel _model;
public ViewModel(LoginModel model)
{
_model = model;
}

public bool AuthResult
{
get
{
return _model.Authenticated;
}
set
{
_model.Authenticated = value;
NotifyPropertyChanged("AuthResult");
}
}
}

<Button Content="Button" IsEnabled="{Binding AuthResult}" />

The benefit of using this latter approach is that view has no dependency upon the model class. It binds to the view model class only and this is how the MVVM design pattern typically is meant to be implemented.

But if you do bind to a (wrapper) property of the view model and want the view to be updated whenever a property of the model class is set, the model has to notify the view model that it has changed one way or another, i.e. it has to raise some kind of event or similar. And this typically means implementing the INotifyPropertyChanged interface. The view model can then subscribe to the PropertyChanged event of the model and raise its own PropertyChanged event for the data bound property whenever the model is updated, e.g.:

public class ViewModel
{
private readonly LoginModel _model;

public ViewModel(LoginModel model)
{
if (model == null)
throw new ArgumentNullException("model");

_model = model;
_model.PropertyChanged += OnModelChanged;
}

private void OnModelChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "Authenticated")
NotifyPropertyChanged("AuthResult");
}

public bool AuthResult
{
get
{
return _model.Authenticated;
}
set
{
_model.Authenticated = value;
NotifyPropertyChanged("AuthResult");
}
}
}

MVVM: Notify view when model status changed

There is no magic bullet solution to this type of problem. Your ViewModel and Model need to be designed in a way that allows the information to propagate to the View; if this is not possible, then the design is flawed and needs to change.

Here's a couple of things you should look into:

  • If the Model's state-modifying methods are documented to execute synchronously, create methods on the ViewModel that forward the action to the Model and then immediately query its state. Use these methods for the implementation of the RelayCommands.
  • If the Model's methods are not synchronous then there should be some mechanism available to the Model's clients to notify them when the methods have completed. This can be done through continuation callbacks, events, or perhaps even with INotifyPropertyChanged.

In MVVM should the ViewModel or Model implement INotifyPropertyChanged?

I'd say quite the opposite, I always put my INotifyPropertyChanged on my ViewModel - you really don't want to be polluting your model with a fairly WPF specific feature like INotifyPropertyChanged, that stuff should sit in the ViewModel.

I'm sure others would disagree, but that's the way I work.



Related Topics



Leave a reply



Submit