Diving Back into WPF: Viewmodel Messaging

Because view models serve as the glue between a particular UI element and the data layer they don’t inherently have any “awareness” of each other. That’s fine when changes to them only have to affect their associated UI element. But there are lots of situations where UI elements need to talk to each other.

Consider a UI for editing an app’s configuration based on a TabControl. Each TabItem is a separate UI control with its own viewmodel controlling some portion of the overall configuration. The container holding the TabControl handles the work of updating a configuration file on disk, reverting to previous settings, etc. Each TabItem viewmodel needs to alert the TabControl viewmodel when its portion of the configuration is updated so it alert the user an unsaved configuration exists. Conversely, the TabControl viewmodel needs to alert all the TabItem viewmodels when the global state has changed (e.g., through a reversion to configuration saved on disk).

This coordination is best handled by messaging. The MVVM toolkit provides a number of way of sending the messages1. The workflow is straightforward:

  • register for the messsages you want your viewmodel to "hear"/receive
  • define message handlers for the messages
  • unregister when you no longer need/want to "hear"/receive the messages

The toolkit provides a base viewmodel class — ObservableRecipient — which has the plumbing to handle all of this2.

The one thing I find I trip over, constantly, using the MVVM messaging system is activating it. By default viewmodels derived from ObservableRecipient don’t listen to anything…even when you’ve carefully defined the messages they should be listening to3. You turn your viewmodel on by setting IsActive to true and turn it off by setting IsActive to false4.

Here’s an example (details omitted for clarity):

public MainViewModel()
{
    IsActive = true;
}

protected override void OnActivated()
{
    base.OnActivated();

    Messenger.Register<MainViewModel, CloseModalWindowMessage, string>( this, "primary", CloseModalWindowMessageHandler );
}

protected override void OnDeactivated()
{
    base.OnDeactivated();
    Messenger.UnregisterAll(this);
}

private void CloseModalWindowMessageHandler( MainViewModel recipient, CloseModalWindowMessage cmwMesg )
{
    switch( cmwMesg.Window )
    {
        case DialogWindow.Options:
            _optionsWin?.Close();
            break;

        case DialogWindow.Processor:
            _procWin?.Close();
            break;
    }
}

The override at lines 6 – 11 registers this viewmodel to listen to CloseModalWindowMessages. I find the registration syntax a bit odd5) in that you have to specify the recipient type, the message type and a token type. I don’t understand why the recipient type can’t be inferred, by default, from the class which is calling the registration method. You reference the message handler in the method call’s arguments.

The token type is apparently intended to allow different instances of the same viewmodel to listen to different messages. As I’ve yet to encounter a situation where there isn’t a single instance of each kind of viewmodel in my app I always set the token type to string and use the value “primary” when it’s a required argument.

Lines 13 – 17 unregister your listeners. This gets called automagically when you set IsActive to false or when the viewmodel is disposed (I think).

The message handler in lines 19 – 30 process the CloseModalWindowMessage. It takes two arguments, the recipient of the message6 and the message itself.

Message classes can be pretty much whatever you need. For me they’re almost always either a set of simple properties or classes with no properties at all. The latter situation occurs when I just need to “hear” that something has happened but I don’t need any details as to just what has happened (e.g., “configuration properties are updated, so offer to save everything to a configuration file on disk”).

In the code snippet shown here the message does contain a property which tells me which of two possible dialog windows need to be closed.


  1. apparently; I’ve yet to need to use anything other than the basic approach. 

  2. It derives from ObservableObject, which provides a viewmodel base apparently lacking only the messaging capabilities. Frankly, since I don’t usually know ahead of time whether I’ll need messaging or not I generally just derive all my viewmodels from ObservableRecipient rather than ObservableObject

  3. There must be an interesting story behind this choice. Or maybe it’s a feature :). 

  4. FWIW I’ve yet to encounter a situation where I don’t want a viewmodel listening for messages as soon as it’s created so I just include IsActive = true; in my constructor calls. 

  5. I may not be using it correctly…but the app works : 

  6. which for me is always the instance declaring the handler since my viewmodels are singletons 

Archives
Categories