Skip to main content
A newer version of this page is available. .

Messenger

  • 9 minutes to read

The Messenger class implements the IMessenger interface and allows you to implement the capability of exchanging messages between modules.

Getting Started With Messenger

Examine how to use Messenger with a simple sample.

Imagine that you have a database that can be modified from multiple modules. One module needs to be notified about database modifications that may happen from other modules (for example, adding, removing, and changing a record).

The code sample below demonstrates how to create a message:

public enum MessageType { Added, Deleted, Changed }
public class Message {
    public MessageType MessageType { get; private set; }
    public object RecordID { get; private set; }
    public Message(object recordID, MessageType messageType) {
        RecordID = recordID;
        MessageType = messageType;
    }
}

You can then use the IMessenger.Register method to subscribe to the message from anywhere in the application.

public class Module1 {
    public Module1() {
        Messenger.Default.Register<Message>(this, OnMessage);
    }
    void OnMessage(Message message) {
        switch(message.MessageType) {
            case MessageType.Added:
                //...
                break;
            case MessageType.Changed:
                //...
                break;
            case MessageType.Deleted:
                //...
                break;
            default:
                throw new NotImplementedException();
        }
    }
}

Use the IMessenger.Send method to send a message.

public class Module2 {
    void SendMessage() {
        Messenger.Default.Send(new Message(0, MessageType.Added));
    }
}

As you can see, this approach implements module interaction without reference to a module’s code. Even if you remove Module1 or Module2 while developing the application, it will not cause errors and the entire system will continue to function.

Also, there is no need to unsubscribe from messages, because the Default Messenger works with weak references, which do not cause memory leaks.

Default Messenger

The Messenger class provides the static Default property. By default, this property contains a Messenger instance that is not multi-thread safe and stores weak references. Typically, you use this Default Messenger to send messages and subscribe to them.

If necessary, you can create a local Messenger instance or change the default IMessenger implementation by setting the static Messenger.Default property. For instance, the following code replaces the Default Messenger by a Messenger that supports multi-threading.

public partial class App : Application {
    public App() {
        Messenger.Default = new Messenger(isMultiThreadSafe: true, actionReferenceType: ActionReferenceType.WeakReference);
    }
}

Sending and Receiving Messages

  • You can use multiple approaches to subscribe to messages and receive them. The easiest one is to use the following methods.
public static void Register<TMessage>(this IMessenger messenger, object recipient, Action<TMessage> action);
public static void Send<TMessage>(this IMessenger messenger, TMessage message);

For instance:

public class Message {
    //...
}
public class Recipient {
    public Recipient() {
        Messenger.Default.Register<string>(this, OnMessage1);
        Messenger.Default.Register<Message>(this, OnMessage2);
    }
    void SendMessages() {
        Messenger.Default.Send("test");
        Messenger.Default.Send(new Message());
    }
    void OnMessage1(string message) {
        //...
    }
    void OnMessage2(Message message) {
        //...
    }
}
  • If you subscribe to a message that has the Class type, you can customize whether your handler should be invoked if an inherited message is sent.
public static void Register<TMessage>(this IMessenger messenger, object recipient, bool receiveInheritedMessagesToo, Action<TMessage> action);
public static void Send<TMessage>(this IMessenger messenger, TMessage message);
public class InheritedMessage : Message {
    //...
}
public class Recipient {
    public Recipient() {
        //Inherited messages are not processed with this subscription
        Messenger.Default.Register<Message>(
            recipient: this, 
            action: OnMessage);
        //Inherited messages are processed with this subscription
        Messenger.Default.Register<Message>(
            recipient: this, 
            receiveInheritedMessagesToo: true,
            action: OnMessage);
    }
    void SendMessages() {
        Messenger.Default.Send(new Message());
        Messenger.Default.Send(new InheritedMessage());
    }
    void OnMessage(Message message) {
        //...
    }
}
  • You can separate messages with a token. To do this, use the following methods.
public static void Register<TMessage>(this IMessenger messenger, object recipient, object token, Action<TMessage> action);
public static void Send<TMessage>(this IMessenger messenger, TMessage message, object token);

The code below demonstrates a situation in which message handlers are only invoked when a message with an appropriate token is sent.

public enum MessageToken { Type1, Type2 }
public class Recipient {
    public Recipient() {
        Messenger.Default.Register<Message>(
            recipient: this, 
            token: MessageToken.Type1,
            action: OnMessage1);
        Messenger.Default.Register<Message>(
            recipient: this, 
            token: MessageToken.Type2,
            action: OnMessage2);
    }
    void SendMessages() {
        Messenger.Default.Send(message: new Message(), token: MessageToken.Type1);
        Messenger.Default.Send(message: new Message(), token: MessageToken.Type2);
    }
    void OnMessage1(Message message) {
        //...
    }
    void OnMessage2(Message message) {
        //...
    }
}

Unregistering Message Handlers

If you use the Default Messenger, there is no need to unregister message handlers, because the Default Messenger works with weak references, which means that message handlers do not lead to memory leaks.

If you use a Messenger instance that works with strong references…

Messenger messenger = new Messenger(isMultiThreadSafe: false, actionReferenceType: ActionReferenceType.StrongReference);

… then it is necessary to perform unregistration. There are two methods for unregistering from messages.

//Performs unregistering from a specific message
void IMessenger.Unregister<TMessage>(object recipient, object token, Action<TMessage> action);
//Unregister a recipient from all messages
void IMessenger.Unregister(object recipient);

Use Of Closures in Message Handlers

The Default Messenger stores message handlers as follows: the handler is separated by the instance that owns the delegate and the method itself. The instance is stored as a weak reference, which doesn’t require unregistering the message handlers and doesn’t cause memory leaks. However, this imposes a limitation for lambda expressions with outer variables (closures): if an intermediate object created by the compiler for a lambda expression is only referenced by the Default Messenger (which stores this object with a weak reference), nothing prevents the garbage collector to collect the intermediate object, and your message handler may never be invoked.

In the code below, the lambda method refers to the text variable that is defined outside the lambda. In this case, this lambda can be collected and never called.

public class Recipient {
    public Recipient(string text) {
        Messenger.Default.Register<Message>(this, x => {
            var str = text;
            //...
        });
    }
}

To fix this issue, declare your text variable as a property/property at the level of the object from which subscription is performed. For example:

public class Recipient {
    string MyProperty {get; set;}
    public Recipient(string text) {
        MyProperty = text;
        Messenger.Default.Register<Message>(this, x => {
            var str = MyProperty;
            //...
        });
    }
}

Another way is to store the message handler as follows:

public class Recipient {
    Action<Message> messageHandler;
    public Recipient(string text) {
        messageHandler = x => {
            var str = text;
            //...
        };
        Messenger.Default.Register<Message>(this, messageHandler);
    }
}

If your lambda does not use outer variables, there are no limitations.