Introduction
MVVM Light is a good MVVM design pattern library for .NET developers. For me, the main use of a messenger is because it allows for communication between ViewModels. Let's say you have a viewmodel
that is used to provide business logic to a search function and three viewmodels on your page/window that want to process the search to show output, the messenger would be the ideal way to do this in a loosely-bound way. A question from stackoverflow.com
was answered perfectly why and when we should use MVVM Light messenger (the answer to the question is PSed
at the end of this article). See examples of Laurent Bugnion’s website to learn how to use
the MVVM Light messenger.
Background
In fact, this tip will be useful for myself that I should really master Mediator Design Pattern. So here, I’ll write my comprehension of Laurent Bugnion’s code for Messenger.
Using the Code
The code source of part Messenger of MVVM Light is in the GalaSoft.MvvmLight (NET35) package. It seems that later version of MvvmLight doesn't change
the Messenger’s code.
-
void Register<TMessage>(object recipient, object token, bool receiveDerivedMessagesToo, Action<TMessage> action);
-
void Send<TMessage>(TMessage message, object token);
These are the two most often used functions in my projects. Register
registers a recipient for a type of message TMessage
. The action parameter will be executed when a corresponding message is sent. Registering a recipient does not create a hard reference to it, so if this recipient is deleted, no memory leak is caused.
Take a look at the implementation of the Register
function. In
the case where we don't receive derived messages, a variable type of Dictionary<Type, List<WeakActionAndToken>>
is there: recipients
, which is the reference of a local variable _recipientStrictAction
. Variable recipients
are locked, because it references a very important local variable which records all registered types in the messenger system. If
the entered TMessage
type didn't exist in the recipients list, then add it to the list.
Each type of TMessage
contains a list of WeakActionAndToken
s which contains
a WeakAction
and a Token
.
private struct WeakActionAndToken
{
public WeakAction Action;
public object Token;
}
Send
sends a message to the registered recipients. The message will reach only recipients that
are registered for this message type using one of the Register
methods, and that are of the targetType
.
public virtual void Send<TMessage>(TMessage message, object token)
{
SendToTargetOrType(message, null, token);
}
In the code, it searches in the list of recipients until it finds the same type of TMessage
. It takes all list of WeakActionAndToken
s of a recipient. Here, we set messageTargetType
to null
.
private static void SendToList<TMessage>(TMessage message,
IEnumerable<WeakActionAndToken> weakActionsAndTokens, Type messageTargetType, object token)
In the function SendToList
, we get TMessage
’s WeakActionAndToken
list. For each item in the WeakActionAndToken
list, it has a Token
and a WeakAction
object. For example, we have two classes A
and B
, class A
wants to receive messages from B
. We need to create a common TMessage
for transferring message from B
to A
, we call it MessageBToA
(it is a class).
A
creates a receiver for receiving B
’s message, so A
registers a TMessage
of type MessageBToA
into the list of _recipientsStrictAction
. So in the MVVM Light’s messenger system of program conserves a message : MessageBToA
-> List<WeakActionAndToken> { [WeakAction { recipient = object A, action = Action<MessageBToA>}, Token = static object ] }
.
B
is ready for sending a message to A
. But if B
didn't get a object of A
, what should B
do then? B
sends a cast message to all targets which is registered in the MessageBToA
message system.
Now we analyse function SendToList
. In _recipientsStrictAction
, B
searches item whose type is MessageBToA
. B
found that there is an item of type MessageBToA
. There is only one item in the list of WeakActionAndToken
of type MessageBToA
, B
has a good token to access this item. B
doesn’t care to whom the message will be sent, it just wants to send its message to A
(whatever anyone else can receive), so the program invokes registered Action<MessageBoToA>
created by A
with B
’s message content.
Technically, the messenger system is realized with using Action<T>
. Receiver created an Action<TMessage>
and affects an implementation of method of this action. Sender get this Action<TMessage>
and gives TMessage
a real instance to be the parameter of this action. We just need to get this action’s MethodInfo
and invoke it. Once method is invoked, the function which is already attached will be executed.
public class WeakAction
{
...
public void Execute()
{
...
Method.Invoke(actionTarget, null);
...
}
}
At last, there is one thing that is very important, it is that for maintaining these registered messengers, Messenger
class has to be a singleton class.
P.S.: Proper way of using MVVM Light Messenger (Ref from stackoverflow.com)
For me, the main use of a messenger is because it allows for communication between viewModels. Let's say you have a viewmodel that is used to provide business logic to a search function and 3 viewmodels on your page/window that want to process the search to show output, the messenger would be the ideal way to do this in a loosely-bound way.
The viewmodel that gets the search data would simply send a “search” message that would be consumed by anything that was currently registered to consume the message.
The benefits here are:
- Easy communication between viewmodels without each viewmodel having to know about each other
- I can swap out the producer without affecting a consumer.
- I can add more message consumers with little effort.
- It keeps the viewmodels simple
Edit: So, what about services?
ViewModels are all about how to present data to the UI. They take your data and shape it into something that can be presented to your View. ViewModels get their data from services.
A service provides the data and/or business logic to the ViewModel. The services job is to service business model requests. If a service needs to communicate/use other services to do its job, these should be injected into the service using dependency injection. Services would not normally communicate with each other using a messenger. The messenger is very much about horizontal communication at the viewmodel level.
One thing I have seen done is to use a messenger as a mediator (mediator description), where instead of injecting the service directly into a viewmodel, the messenger is injected into the viewmodel instead. The viewmodel subscribes to an event and receives events containing models from the event. This is great if you’re receiving a steady flow of updates or you’re receiving updates from multiple services that you want to merge into a single stream.
Using a messenger instead of injecting a service when you’re doing request/response type requests doesn’t make any sense as you'll have to write more code to do this, that you’d have to write just injecting the service directly and it makes the code hard to read.
Looking at your code above, imagine if you had to write an event for each method
in there (Navigate
, CanNavigate
, GoBack
, GoForward
, etc.). You’d end up with a lot of messages. Your code would also be harder to follow.
Points of Interest
After studying a little from Laurent's code, I know better about using Action
and Func
in C# projects.