Home > C#, MVVM, Universal Windows Platform > Using MVVM Light Messenger with Template 10

Using MVVM Light Messenger with Template 10

30/11/2015

In previous posts we shown how to integrate MVVM Light with Template 10. We still need to talk about an important component of the toolkit, the Messenger. It allows for communication within the application, i.e. between View Models, Views and View Models, and so on.

We can access the Messenger from anywhere in our code, using its static Messenger.Default property. So, for example, we can easily integrate it in a View Model even if it inherits from the Template 10 ViewModelBase class (instead of the MVVM Light base class).

The first thing we can do with the Messenger is sending object, i.e. to execute code in the View Model in response to View event handlers:

private void GridView_ItemClick(object sender, ItemClickEventArgs e)
{
    // Sends a message with a Person object.
    var person = e.ClickedItem as Person;
    Messenger.Default.Send(person);
}

// In the View Model...
// Registers for incoming Person messages.
Messenger.Default.Register<Person>(this, (p) =>
{
    // Works with the Person object.
});

MVVM Light provides also a built-in class, NotificationMessage, that we can use if we just want to send string messages:

// Sends a notification message with a string content.
Messenger.Default.Send(new NotificationMessage("CloseApp"));

// Registers for incoming Notification messages.
Messenger.Default.Register<NotificationMessage>(this, (message) =>
{
    // Checks the actual notification.
    switch (message.Notification)
    {
        case "CloseApp":
            break;

        default:
            break;
    }
});

At lines 8-15, we check the Notification property to determine the specific message that has been sent. Of course, we can use constants to avoid the repetition of string values.

We can combine both of these scenarios and use the generic version of NotificationMessage to send an object along with a string message, which for example specifies the action that we want to perform:

private void GridView_ItemClick(object sender, ItemClickEventArgs e)
{
    // Sends a message with a Person object.
    var person = e.ClickedItem as Person;

    // Sends a notification message with a Person as content.
    Messenger.Default.Send(new NotificationMessage<Person>(person, "Select"));
    //Messenger.Default.Send(new NotificationMessage<Person>(person, "Update"));
}

// In the View Model...
// Registers for incoming Notification messages.
Messenger.Default.Register<NotificationMessage<Person>>(this, (message) =>
{
    // Gets the Person object.
    var person = message.Content;

    // Checks the associated action.
    switch (message.Notification)
    {
        case "Select":
            break;

        case "Update":
            break;

        default:
            break;
    }
});

The generic version of NotificationMessage exposes a Content property (line 16) that contains the actual object. So we can retrieve it and use the Notification value to determine which action to execute (lines 19-29).

In some cases, we would need to execute an action back on the sender when the message is received. For these scenarios, MVVM Light provides the NotificationMessageAction class, which allows to specify a callback method that can be executed by the recipient to notify the sender that the message has been processed. With this object, however, we can only send a notification string message. So what about if we want to send also a content class, like in the generic NotificationMessage?

First of all, we need to define a class that inherits from the standard NotificationMessageAction class:

public class NotificationMessageAction<T, TCallbackParameter> 
    : NotificationMessageAction<TCallbackParameter>
{
    public T Content { get; }

    public NotificationMessageAction(T content, string notification, 
        Action<TCallbackParameter> callback)
      : base(notification, callback)
    {
        Content = content;
    }
}

Basically, we have added the generic Content property (line 4). Then, we can write something like this:

private void GridView_ItemClick(object sender, ItemClickEventArgs e)
{
    // Sends a message with a Person object.
    var person = e.ClickedItem as Person;

    // Sends a notification message with a Person as content, 
    // a string message and a callback to be executed at the end.
    Messenger.Default.Send(new NotificationMessageAction<Person, int>(person, 
        "Update", (response) => 
    {
        // "response" contains the response (int in this example) from the
        // receiver.
    }));
}

// In the View Model...
// Registers for incoming NotificationMessageAction messages.
Messenger.Default.Register<NotificationMessageAction<Person, int>>(this, 
    (message) =>
{
    // Gets the Person object.
    var person = message.Content;

    // Checks the associated action.
    switch (message.Notification)
    {
        case "Update":
            break;

        default:
            break;
    }

    // Executes the callback.
    message.Execute(42);
});

At lines 8-13 we send our NotificationMessageAction message, specifying that Content is a Person and the callback parameter is int. Then, in the View Model we process the message as we did in the previous example (lines 21-32) and finally we execute the callback (line 35): this instruction will cause the action defined at lines 10-13 to be executed.

If we use an IoC Container for View Models (like MVVM Light SimpleIoC), we must remember that Messenger registrations remain active even if we change View (because View Models actually stay in memory), so they always receive notifications. Instead, we usually want that a View Model receives notification only if its corresponding View is active.

To overcome this problem, we simply can register messages in the Template 10 OnNavigatedTo method and unregister them in OnNavigatedFromAsync:

public override void OnNavigatedTo(object parameter, NavigationMode mode,
    IDictionary<string, object> state)
{
    // Registers for incoming Person messages.
    Messenger.Default.Register<Person>(this, (p) =>
    {
        
    });

    base.OnNavigatedTo(parameter, mode, state);
}

public override Task OnNavigatedFromAsync(IDictionary<string, object> state, 
    bool suspending)
{
    // Unregisters all messages registered by this receiver.
    Messenger.Default.Unregister(this);

    return base.OnNavigatedFromAsync(state, suspending);
}

To unregister a messenger recipient completely, we simply need to call the Unregister method, passing the receiver reference (line 17).

  1. No comments yet.
  1. 30/11/2015 at 17:07
Comments are closed.
%d bloggers like this: