Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

WPF MVVM Correct way to fire event on view from ViewModel

Tags:

In my WPF application I have 2 Windows (both Windows have their own ViewModel):

  1. Application's Main Window that displays list with bunch of words (bound to MainViewModel)

  2. Dialog Window that allows users add new items to the list (bound to AddWordViewModel)

MainViewModel has Articles property of List(this collection is populated by one of the service classes) bound to Main Window's ListBox

AddWordViewModel has SaveWordCommand that is bound to Add Word Dialog's Save button. It's task is to take text entered by user and pass it to service class.

After user clicks on on Save button I need notify MainViewModel to reload Articles from service.

My idea was to expose public command in MainViewModel and execute it from AddWordViewModel

What is correct way to implement it?

Thank You!

like image 309
Daniil Harik Avatar asked Apr 28 '09 17:04

Daniil Harik


People also ask

Can ViewModel have reference view?

In "pure" MVVM, the ViewModel shouldn't really reference the View. It's often convenient, however, to provide some form of interface in the View whereby the ViewModel can interact with it.

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.


1 Answers

Event Aggregators are quite a nice way of solving this type of problem. Basically there is a centralised class (for simplicities sake let's say it's a Singleton and face the possible wrath of the anti-singleton guys) that is responsible for transferring events from one object to another. With your class names the usage may look like:

public class MainViewModel {     public MainViewModel()     {         WordAddedEvent event = EventAggregator.Instance.GetEvent<WordAddedEvent>();         event.Subscribe(WordAdded);     }      protected virtual void WordAdded(object sender WordAddedEventArgs e)     {         // handle event     } }  public class AddWordViewModel {         //From the command     public void ExecuteAddWord(string word)     {         WordAddedEvent event = EventAggregator.Instance.GetEvent<WordAddedEvent>();         event.Publish(this, new WordAddedEventArgs(word));     } } 

The advantage of this pattern is that you can very easily expand your application to have multiple ways of creating words and multiple ViewModels that are interested in words that have been added and there is no coupling between the two so you can add and remove them as you need to.


If you want to avoid the singleton (and for testing purposes I would suggest you do) then it may be worth looking into dependency injection, though that really is a whole other issue.


Okay, final thought. I see from re-reading your question that you already have some kind of Word Service class that handles the retrieval and storage of the Word objects. There is no reason that the service can't be responsible for raising the event when the new word is added since both ViewModels already are coupled to it. Though I'd still suggest the EventAggregator is more flexible and a better solution, but YAGNI may apply here

public class WordService {     public event EventHandler<WordAddedEventArgs> WordAdded;      public List<string> GetAllWords()     {         //return words     }      public void SaveWord(string word)     {         //Save word         if (WordAdded != null) WordAdded(this, new WordAddedEventArgs(word));         //Note that this way you lose the reference to where the word really came from         //probably doesn't matter, but might     } }  public class MainViewModel {     public MainViewModel()     {         //Add eventhandler to the services WordAdded event     } } 

What you want to avoid doing though is introducing the coupling between ViewModels that you'll create by calling a command on one ViewModel with the other, this will severely limit your options for expanding the application (what if a second ViewModel became interested in new words, is it now the AddWordViewModel's responsibility to tell that one too?)

like image 178
Martin Harris Avatar answered Oct 19 '22 01:10

Martin Harris