views:

2050

answers:

1

Hello,

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!

+8  A: 

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?)

Martin Harris
Thank You for very detailed answer. Will have to dig into it :) A lot of fun coming up :)
Daniil Harik