MVVM Light Toolkit Messenger V2

Update: MVVM Light Toolkit V2 has been released. Please use the new installer to upgrade your installation.

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:

// Somewhere
Messenger.Default.Register<string>(this, DoSomething);

// Further
private void DoSomething(string message)
{
    // ...
}

// Somewhere else
Messenger.Default.Send(“Hello world”);

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.

 

Print | posted on Sunday, September 27, 2009 1:16 AM

Feedback

# re: MVVM Light Toolkit Messenger V2 beta

left by Alexey Zakharov at 9/28/2009 3:41 AM Gravatar
Hi,

It seems to me that PRISM Event Aggregator do the same job long time ago? What is the difference between EA and your messenger?

Thanks,
Alexey

# re: MVVM Light Toolkit Messenger V2 beta

left by Laurent Bugnion at 9/28/2009 4:44 AM Gravatar
Hi Alexey,

They are both very similar in the intent, but the Messenger's syntax is simpler, and the component is very lightweight. It should be easier to start with the Messenger (or to integrate it to an existing application). For the records, Glenn Block (who worked a lot on Prism) helped me with the Messenger syntax and was an early tester too.

Thanks for your comment,
Laurent

# re: MVVM Light Toolkit Messenger V2 beta

left by Sam Azish at 9/29/2009 4:31 AM Gravatar
not sure about PRISM but I prefer MS's PropertyChangedEventManager and CollectionChangedEventManager in my ViewModelBase to register/un-register for property changes which inherits from WeakEventManager and (like your solution) uses weak events as well as weak references. As far as I know even WPF internally uses PropertyChangedEventManager.

# re: MVVM Light Toolkit Messenger V2 beta

left by Laurent Bugnion at 9/29/2009 7:29 AM Gravatar
Hi Sam,

These class are quite similar in their intent, however the Messenger V2 does not pose any constraint on the message recipient, in the contrary of PropertyChangedEventManager and CollectionChangedEventManager, which require the recipient to implement IWeakEventListener. You could say that the Messenger is a lighter (and simpler to use) implementation of the same pattern.

I hope this helps,
Thanks,
Laurent

# re: MVVM Light Toolkit Messenger V2 beta

left by David Cuccia at 9/29/2009 8:02 AM Gravatar
Hi Laurent,

I'm not sure if this is the correct place to post this, but I was wondering why all the disciples don't try to contribute to the same MVVM framework. Seems like Josh, Marlon, Karl, and Pete also have similar tools (though of course unique in many ways). Seems like the MVVM Framework on CodePlex is a good place to start. "Contrib" libraries with each person's unique features could dissolve into the trunk over time as they mature. What are your thoughts?

Thanks,
David Cuccia

# re: MVVM Light Toolkit Messenger V2 beta

left by Laurent at 9/29/2009 9:10 AM Gravatar
Hi David,

Yeah it is a good point. If you have been following the discussions on that group in the past year or so, we have been talking about undertaking a common framework. It didn't quite pan out. I think that the main reason is that we have different reasons to use (and make) such a framework. For me, the main reason is really Blendability (design time data, specifically), with testability of the UI a close second. For others, validation plays a very large role in their adoption of the MVVM pattern, etc. In addition, to be useful to me, a framework needs to work in Silverlight as well as in WPF, while others do not care about Silverlight (yet?) and find it too limited. I cannot rely on WPF-only mechanisms in my framework, but others do because their craft is in a different area than mine (I am, as you might know if you follow this blog, an integrator and working mostly around the user experience and the top layers of the application).

For what it is worth, there is quite an intense collaboration between some of the disciples and me on this toolkit, though maybe not directly visible on the group (we talk per email and MSN). The toolkit I provide is a "blend" (pun intended) of code and ideas coming from different sources. The credits section on the Get Started page (http://www.galasoft.ch/mvvm/getstarted) lists some of the major contributors, most of them from the disciples group. I have fished many ideas and solved many problems on the group.

Lastly, I would be a bit reluctant to engage in a large scale operation to provide a "one and only" MVVM framework. To be blunt, it sounds like a lot less fun than what I am doing now, mostly in the wee hours after the family is in bed. I would probably be more enclined to work on a larger scale team if I didn't think that much of what I have here is going to flow into the framework eventually. The good news is that many at Microsoft are aware of this effort, and I think we will see improvements in the framework (WPF and Silverlight) directly that should make some of this toolkit redundant. This is why I put a lot of effort in keeping it "light", because I am pretty sure it will be obsolete some day.

Which doesn't mean that I don't do my best to deliver quality code, and to listen to community feedback :)

Thanks a lot for your input!
Cheers,
Laurent

# re: MVVM Light Toolkit Messenger V2

left by Igor at 5/17/2010 10:07 PM Gravatar
Hello Laurent,

I use your wonderful framework. It is small, but still capable to do a lot of things. I believe your messenger lacking one simple feature. It is async Messenger's calls on UI thread. Since Messenger.Default.Send is sync, thread can be locked. I ended up with such code
DispatcherHelper.UIDispatcher.BeginInvoke(new ThreadStart(() => Messenger.Default.Send(new NotificationMessage(this, "Show Modal Dialog"))));
Your messenger reminds me Win32API function SendMessage. There is async functionality as well.

Regards,
Igor.

# re: MVVM Light Toolkit Messenger V2

left by Laurent at 5/18/2010 8:29 PM Gravatar
Hi,

I understand the concern. I deliberately chose not to include dispatching in the Messenger because I didn't want to hide this. I am very wary of "magic" especially when it comes to multithreading. I do agree that the code is annoying to write though. I might reconsider in a next version.

Note that the code can be simplified a little with

DispatcherHelper.CheckInvokeOnUI(() => Messenger.Default.Send(new NotificationMessage(this, "Show Modal Dialog")));

Still annoying but a few less characters ;)

Cheers and thanks for the feedback.
Laurent

# re: MVVM Light Toolkit Messenger V2

left by Igor at 5/19/2010 10:39 AM Gravatar
Thanks for answering. I want to state to things.
First.

DispatcherHelper.CheckInvokeOnUI(() => Messenger.Default.Send(new NotificationMessage(this, "Show Modal Dialog")));

is not equal to

DispatcherHelper.UIDispatcher.BeginInvoke(new ThreadStart(() => Messenger.Default.Send(new NotificationMessage(this, "Show Modal Dialog"))));

,because

public static void CheckBeginInvokeOnUI(Action action)
{
if (UIDispatcher.CheckAccess())
{
action();
}
else
{
UIDispatcher.BeginInvoke(action, new object[0]);
}
}

Is tricky func, which calls sync if it is UI thread and async if not, but i need async anyway.

Second. Messenger is some kind of Observer pattern. It solve the same issue as .net event but with different implementation. Also it can be used to invoke methods. It is quite easy to understand how to use it, but not so obvious where. In my solution I use it only for ViewModel/View communication, it is forbidden in all other places. How do you see the area of Messenger application?

Regards,
Igor.

# re: MVVM Light Toolkit Messenger V2

left by Laurent at 5/19/2010 10:47 AM Gravatar
Hi,

Ah yes you are right of course. I didn't realize you wanted asynchronous always, but it makes sense.

Adding this feature to the Messenger would complicate the API. Suddenly I need a parameter to know if you want to send the message asynchronously, synchronously (if possible) and with which priority. I think Prism does this in the EventAggregator. Believe I hesitated a LOT before deciding not to dispatch within the Messenger (easier to not do it first, and if there is really a need to add it, that would not be a breaking change while the other direction would be). I am still not completely sold on adding dispatch to the Messenger.

Yes the Messenger is a loosely coupled Observer (the first idea actually comes from the mediator pattern, but I think that the messenger implementation is not really a mediator, and more of an observer). I use it where decoupled communication must take place. Typically I use it between VM and view, and between VM and VM. Strictly speaking you can use it in multiple places, but I always recommend people to be careful with it. It is a powerful tool, but because of the very loose coupling, it is easy to lose the overview on what you are doing. Use it where it makes sense, but don't replace all your events and commands with messages.

Cheers,
Laurent

# re: MVVM Light Toolkit Messenger V2

left by Igor at 5/19/2010 11:06 AM Gravatar
Hi,

I understand your idea and I like it. Keep it small and simple. In anyway we have PRISM with lots of stuff there. So, having something lighter such a good idea.

I use Messenger only for communication between VM and View, VM and VM. It is a great pleasure to know, that you use framework as intended :-)
Maybe you should point this somewhere? I used Messenger in one service once, after that I understood, that everything becomes to complicated and untraceable. Thus, somebody can escape such experience.

Regards,
Igor.
Post A Comment
Title:
Name:
Email:
Website:
Comment:
Verification: