Skip to main content

Messenger Class

Implements the IMessenger interface and allows you to exchange messages between application modules.

Namespace: DevExpress.Mvvm

Assembly: DevExpress.WinUI.Mvvm.v23.2.dll

NuGet Package: DevExpress.WinUI

Declaration

public class Messenger :
    IMessenger

Remarks

Run Demo: Messenger Module in the WinUI MVVM Demo

The following example demonstrates how to exchange messages between application modules:

 <UserControl ...
    xmlns:dx="using:DevExpress.WinUI.Core"
    xmlns:ViewModel="using:WinUIApp.ViewModel" ...>

    <StackPanel Orientation="Vertical">
        <!--A Grid bound to the SenderViewModel--> 
        <Grid>
            <Grid.DataContext>
                <ViewModel:SenderViewModel/>
            </Grid.DataContext>
            <!--Click the button to send a message-->
            <Button Content="Send Message" Command="{x:Bind SendMessageCommand}"/>
        </Grid>
        <!--A Grid bound to the ReceiverViewModel--> 
        <Grid>
            <Grid.DataContext>
                <ViewModel:ReceiverViewModel/>
            </Grid.DataContext>
            <TextBlock Text="{x:Bind ReceivedMessage, Mode=OneWay}" />
        </Grid>
    </StackPanel>
</UserControl>
using DevExpress.Mvvm;

namespace MVVMDemo.ViewModelsInteraction {
    // Sender ViewModel
    public class SenderViewModel : BindableBase {
        public DelegateCommand SendMessageCommand { get; }
        public SenderViewModel() {
            SendMessageCommand = new DelegateCommand(() => SendMessage());
        }
        public void SendMessage() {
            Messenger.Default.Send("Hello world!");
        }
    }
    // Receiver ViewModel
    public class ReceiverViewModel : BindableBase {
        public string ReceivedMessage { get => GetValue<string>(); set => SetValue(value); }
        public ReceiverViewModel() {
            Messenger.Default.Register<string>(this, OnMessage);
        }
        void OnMessage(string message) {
            ReceivedMessage = "Received: " + message;
        }
    }
} 

Default Messenger

The static Messenger.Default property returns a default Messenger instance. The default messenger is not multi-thread safe and stores weak references.

Change the Default Messenger

To change the default messenger, create a new Messenger instance and pass it to the static Messenger.Default property.

The following code sample replaces the default Messenger with a multi-thread safe Messenger instance:

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

Send and Receive Messages

Call the Register and Send methods to recieve and send messages.

public static void Register<TMessage>(this IMessenger messenger, object recipient, Action<TMessage> action);
public static void Send<TMessage>(this IMessenger messenger, TMessage message);

The following code sample sends and receives messages with different types:

public class MyMessage {
    //...
}
public class Recipient {
    public Recipient() {
        // Receives messages of the `string` type.
        Messenger.Default.Register<string>(this, OnMessage1);
        // Receives messages of a custom `MyMessage` type.
        Messenger.Default.Register<MyMessage>(this, OnMessage2);
    }
    void SendMessages() {
        // Sends messages of the `string` type.
        Messenger.Default.Send("test");
        // Sends messages of a custom `MyMessage` type.
        Messenger.Default.Send(new MyMessage());
    }
    void OnMessage1(string message) {
        //...
    }
    void OnMessage2(MyMessage message) {
        //...
    }
}

Send and Receive Message Descendants

When you subscribe to a message with a custom type, you can invoke your handler if a message descendant is received.

Use the following methods to work with the messages with custom inherited types:

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);

The code sample below invokes a handler when a message descendant is received:

public class InheritedMessage : MyMessage {
    // ...
}
public class Recipient {
    public Recipient() {
        // Inherited messages are not processed with this subscription
        Messenger.Default.Register<MyMessage>(
            recipient: this, 
            action: OnMessage);
        // Inherited messages are processed with this subscription
        Messenger.Default.Register<MyMessage>(
            recipient: this, 
            receiveInheritedMessagesToo: true,
            action: OnMessage);
    }
    void SendMessages() {
        Messenger.Default.Send(new MyMessage());
        Messenger.Default.Send(new InheritedMessage());
    }
    void OnMessage(MyMessage message) {
        // ...
    }
}

Messages With Tokens

You can invoke message handlers when a message with a particular token is received. Use the following methods to work with messages with tokens:

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 sample below invokes handlers when a message with a specified token is received:

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

Unregister Message Handlers

A Messenger With Weak References

If you use a Messenger with weak references (the Default Messenger), you do not need to unregister message handlers. When you subscribe to messengers with weak references, it does not cause memory leaks.

A Messenger With Strong References

You can use a Messenger instance that works with strong references:

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

To avoid memory leaks, unregister message handlers with any of the following methods:

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

Closures in Message Handlers

The weak reference messenger (like the default messenger) imposes a limitation for lambda expressions with outer variables (closures).

The weak reference messenger refers to a lambda expression with a weak reference. If the garbage collector collects the lambda expression object, the message handler is not 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) {
        // WARNING!
        // The lambda may be collected and never called.
        Messenger.Default.Register<Message>(this, x => {
            var str = text;
            //...
        });
    }
}

To prevent a lambda expression object from being collected, declare the text variable as a property at the subscriber object level:

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

You can also store the message handler:

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

If your lambda expression does not use outer variables, your message handler is invoked without any limitations.

Implements

Inheritance

Object
Messenger
See Also