Click here to Skip to main content
15,887,421 members
Articles / Desktop Programming / WPF
Tip/Trick

Thinking in MVVMLight Messenger

Rate me:
Please Sign up or sign in to vote.
4.86/5 (4 votes)
13 Dec 2013CPOL6 min read 47.1K   19   3
How to think in MVVMLight Messenger.

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.

  • C#
    void Register<TMessage>(object recipient, object token, bool receiveDerivedMessagesToo, Action<TMessage> action);
  • C#
    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 WeakActionAndTokens which contains a WeakAction and a Token.

C#
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.

C#
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 WeakActionAndTokens of a recipient. Here, we set messageTargetType to null.

C#
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.

Messenger

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.

C#
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:

  1. Easy communication between viewmodels without each viewmodel having to know about each other
  2. I can swap out the producer without affecting a consumer.
  3. I can add more message consumers with little effort.
  4. 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.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
France France
A foolish 18 years old boy
Just got an Microsoft C# Specialist Certification. Objectif of this year : MCSD

Comments and Discussions

 
QuestionSystem.AddIn Namespace Pin
JohnMeers8-Mar-16 9:15
JohnMeers8-Mar-16 9:15 
QuestionYes, but Pin
Sacha Barber13-Dec-13 6:23
Sacha Barber13-Dec-13 6:23 
AnswerRe: Yes, but Pin
comiscience21-Dec-13 8:54
comiscience21-Dec-13 8:54 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.