Ever since I released V1.1 of the MVVM Light Toolkit, I have received wonderful and constructive feedback from users. Most of it is flowing into V2, which is almost ready (I am still fighting a little with MSI installers and project/item templates for one feature I am particularly happy about, which is making project templates and item templates available in Expression Blend. That’s right, with V2 you can start Blend, and choose “File / New Project / MVVM Light”, then press F5 to have an application running.

IMHO the most interesting and innovative change in the toolkit is the new Messenger. In V1.1, quite a lot of plumbing as involved to register for messages, and to send messages. You had to implement IMessageRecipient to get messages, and all messages had to inherit the MessageBase class. Let’s face it, this was a bad idea. Thankfully, Glenn Block (of MEF and Prism fame) called me on that, and suggested a much, much cleaner implementation: Get rid of IMessageRecipient and use Actions instead to register for messages. Get rid of MessageBase and allow me to send anything through the Messenger.

The new API

This immediately appealed to me (and to some of my users whom I talked to) and I propose to you the Messenger V2. It implements the IMessenger interface, which should make mocking and testing easier. The interface looks like this:

/// <summary>
/// Registers a recipient for a type of message TMessage. The <see cref="action" />
/// parameter will be executed when a corresponding message is sent.
/// <para>Registering a recipient does not create a hard reference to it,
/// so if this recipient is deleted, no memory leak is caused.</para>
/// </summary>
/// <typeparam name="TMessage">The type of message that the recipient registers
/// for.</typeparam>
/// <param name="recipient">The recipient that will receive the messages.</param>
/// <param name="action">The action that will be executed when a message
/// of type TMessage is sent.</param>
void Register<TMessage>(object recipient, Action<TMessage> action);
/// <summary>
/// Registers a recipient for a type of message TMessage.
/// The <see cref="action" /> parameter will be executed when a corresponding
/// message is sent. See the <see cref="receiveDerivedMessagesToo" /> parameter
/// for details on how messages deriving from TMessage (or, if TMessage is an interface,
/// messages implementing TMessage) can be received too.
/// <para>Registering a recipient does not create a hard reference to it,
/// so if this recipient is deleted, no memory leak is caused.</para>
/// </summary>
/// <typeparam name="TMessage">The type of message that the recipient registers
/// for.</typeparam>
/// <param name="recipient">The recipient that will receive the messages.</param>
/// <param name="receiveDerivedMessagesToo">If true, message types deriving from
/// TMessage will also be transmitted to the recipient. For example, if a SendOrderMessage
/// and an ExecuteOrderMessage derive from OrderMessage, registering for OrderMessage
/// and setting receiveDerivedMessagesToo to true will send SendOrderMessage
/// and ExecuteOrderMessage to the recipient that registered.
/// <para>Also, if TMessage is an interface, message types implementing TMessage will also be
/// transmitted to the recipient. For example, if a SendOrderMessage
/// and an ExecuteOrderMessage implement IOrderMessage, registering for IOrderMessage
/// and setting receiveDerivedMessagesToo to true will send SendOrderMessage
/// and ExecuteOrderMessage to the recipient that registered.</para>
/// </param>
/// <param name="action">The action that will be executed when a message
/// of type TMessage is sent.</param>
void Register<TMessage>(object recipient, bool receiveDerivedMessagesToo, Action<TMessage> action);
/// <summary>
/// Sends a message to registered recipients. The message will
/// reach all recipients that registered for this message type
/// using one of the Register methods.
/// </summary>
/// <typeparam name="TMessage">The type of message that will be sent.</typeparam>
/// <param name="message">The message to send to registered recipients.</param>
void Send<TMessage>(TMessage message);
/// <summary>
/// Sends a message to registered recipients. The message will
/// reach only recipients that registered for this message type
/// using one of the Register methods, and that are
/// of the targetType.
/// </summary>
/// <typeparam name="TMessage">The type of message that will be sent.</typeparam>
/// <typeparam name="TTarget">The type of recipients that will receive
/// the message. The message won't be sent to recipients of another type.</typeparam>
/// <param name="message">The message to send to registered recipients.</param>
void Send<TMessage, TTarget>(TMessage message);
/// <summary>
/// Unregisters a messager recipient completely. After this method
/// is executed, the recipient will not receive any messages anymore.
/// </summary>
/// <param name="recipient">The recipient that must be unregistered.</param>
void Unregister(object recipient);
/// <summary>
/// Unregisters a message recipient for a given type of messages only.
/// After this method is executed, the recipient will not receive messages
/// of type TMessage anymore, but will still receive other message types (if it
/// registered for them previously).
/// </summary>
/// <typeparam name="TMessage">The type of messages that the recipient wants
/// to unregister from.</typeparam>
/// <param name="recipient">The recipient that must be unregistered.</param>
void Unregister<TMessage>(object recipient);
/// <summary>
/// Unregisters a message recipient for a given type of messages and for
/// a given action. Other message types will still be transmitted to the
/// recipient (if it registered for them previously). Other actions that have
/// been registered for the message type TMessage and for the given recipient (if
/// available) will also remain available.
/// </summary>
/// <typeparam name="TMessage">The type of messages that the recipient wants
/// to unregister from.</typeparam>
/// <param name="recipient">The recipient that must be unregistered.</param>
/// <param name="action">The action that must be unregistered for
/// the recipient and for the message type TMessage.</param>
void Unregister<TMessage>(object recipient, Action<TMessage> action);

No Memory Leaks

One neat thing with the Messenger (already in V1 and of course also in V2) is that the objects are referenced using WeakReferences. Even though I recommend to unregister explicitly when you want to delete the message recipient, if you omit to do so, you won’t cause a memory leak, because the Messenger does not keep a hard reference to the recipient.

Backwards compatibility

All V1 methods are still available, but marked as Obsolete. This allows a smooth evolution of existing projects to the new syntax. When you build existing projects with the new toolkit, you will get warnings with a suggestion to move to the new syntax.

The MessageBase class and the deriving message types (GenericMessage, CommandMessage, CommandMessageGeneric, DialogMessage, PropertyChangedMessage) are still available as suggestions for the payload (i.e. you can use them if you like, but you can also build your own messages, or send anything you want (objects, simple value types, etc…). It is perfectly legal to do:

Default Messenger, mocking, testing, IoC

Note that like in V1, you can either use the default Messenger (Messenger.Default) or create your own messengers, for example to create specialized channels of communication. Since the IMessenger interface is now available, you can easily mock, test, use IoC containers, etc…

The Messenger V2 as well as the rest of the toolkit will be available in a few days, and I will also prepare sample applications to demonstrate the use more in-depth.

5 Responses to “MVVM Light Toolkit Messenger V2”

Hi Laurant,
I’m coding an application by using wpf, mvvmlight and modernui.
I have a list of employees and need to select one and update some information in different pages base on selected employee. I’ve done by messaging service but it doesn’t work for first load of my viewmodel but it works well after that. would you please help me how can I solve this issue?

It sounds as if your first message is sent before the viewmodel registered with the Messenger. What you can do to mitigate that is implement a small object that is created as soon as the application starts, and registers with the Messenger, and acts as a cache for “old messages”. This way, newly created objects like your viewmodel can get older messages from this cache.

Of course the simpler alternative is to make sure that your viewmodel is created as early as possible when the app starts, but of course this is not always doable.

One typical way to solve this is to use RaisePropertyChanged(…, true). The last Boolean will trigger the sending of a PropertyChangedMessage by the Messenger. In the other VM, subscribe to Messenger.Default.Register
>(this, HandlePropertyChangedMessage);