You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/mvvm/Messenger.md
+50-19Lines changed: 50 additions & 19 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -9,15 +9,18 @@ dev_langs:
9
9
10
10
# Messenger
11
11
12
-
The [`Messenger`](https://docs.microsoft.com/dotnet/api/microsoft.toolkit.mvvm.Messaging.Messenger) class (with the accompanying [`IMessenger`](https://docs.microsoft.com/dotnet/api/microsoft.toolkit.mvvm.Messaging.IMessenger) interface) can be used to exchange messages between different objects. This can be useful to decouple different modules of an application without having to keep strong references to types being referenced. It is also possible to send messages to specific channels, uniquely identified by a token, and to have different messengers in different sections of an application.
12
+
The [`IMessenger`](https://docs.microsoft.com/dotnet/api/microsoft.toolkit.mvvm.Messaging.IMessenger) interface is a contract for types that can be used to exchange messages between different objects. This can be useful to decouple different modules of an application without having to keep strong references to types being referenced. It is also possible to send messages to specific channels, uniquely identified by a token, and to have different messengers in different sections of an application. The MVVM Toolkit provides two implementations out of the box: [`WeakReferenceMessenger`](https://docs.microsoft.com/dotnet/api/microsoft.toolkit.mvvm.Messaging.WeakReferenceMessenger) and [`StrongReferenceMessenger`](https://docs.microsoft.com/dotnet/api/microsoft.toolkit.mvvm.Messaging.StrongReferenceMessenger): the former uses weak references internally, offering automatic memory management for recipients, while the latter uses strong references and requires users to manually unsubscribe their recipients when they're no longer needed, but in exchange for that offers better performance and far less memory usage.
13
13
14
14
## How it works
15
15
16
-
The `Messenger` type is responsible for maintaining links between recipients (receivers of messages) and their registered message types, with relative message handlers. Any object can be registered as a recipient for a given message type using a message handler, which will be invoked whenever the `Messenger` instance is used to send a message of that type. It is also possible to send messages through specific communication channels (each identified by a unique token), so that multiple modules can exchange messages of the same type without causing conflicts. Messages sent without a token use the default shared channel.
16
+
Types implementing `IMessenger` are responsible for maintaining links between recipients (receivers of messages) and their registered message types, with relative message handlers. Any object can be registered as a recipient for a given message type using a message handler, which will be invoked whenever the `IMessenger` instance is used to send a message of that type. It is also possible to send messages through specific communication channels (each identified by a unique token), so that multiple modules can exchange messages of the same type without causing conflicts. Messages sent without a token use the default shared channel.
17
17
18
-
There are two ways to perform message registration: either through the `IRecipient<TMessage>` interface, or using an `Action<TMessage>` delegate acting as message handler. The first lets you register all the handlers with a single call to the `RegisterAll` extension, which automatically registers the recipients of all the declared message handlers, while the latter is useful when you need more flexibility or when you want to use a simple lambda expression as a message handler.
18
+
There are two ways to perform message registration: either through the `IRecipient<TMessage>` interface, or using a `MessageHandler<TRecipient, TMessage>` delegate acting as message handler. The first lets you register all the handlers with a single call to the `RegisterAll` extension, which automatically registers the recipients of all the declared message handlers, while the latter is useful when you need more flexibility or when you want to use a simple lambda expression as a message handler.
19
19
20
-
Similar to the `Ioc` class, `Messenger` exposes a `Default` property that offers a thread-safe implementation built-in into the package. It is also possible to create multiple `Messenger` instances if needed, for instance if a different one is injected with a DI service provider into a different module of the app (for instance, multiple windows running in the same process).
20
+
Both `WeakReferenceMessenger` and `StrongReferenceMessenger` also expose a `Default` property that offers a thread-safe implementation built-in into the package. It is also possible to create multiple messenger instances if needed, for instance if a different one is injected with a DI service provider into a different module of the app (for instance, multiple windows running in the same process).
21
+
22
+
> [!NOTE]
23
+
> Since the `WeakReferenceMessenger` type is simpler to use and matches the behavior of the messenger type from the `MvvmLight` library, it is the default type being used by the [`ObservableRecipient`](ObservableRecipient.md) type in the MVVM Toolkit. The `StrongReferenceType` can still be used, by passing an instance to the constructor of that class.
21
24
22
25
## Sending and receiving messages
23
26
@@ -33,34 +36,58 @@ public class LoggedInUserChangedMessage : ValueChangedMessage<User>
Let's imagine this message type being used in a simple messaging application, which displays a header with the user name and profile image of the currently logged user, a panel with a list of conversations, and another panel with messages from the current conversation, if one is selected. Let's say these three sections are supported by the `HeaderViewModel`, `ConversationsListViewModel` and `ConversationViewModel` types respectively. In this scenario, the `LoggedInUserChangedMessage` message might be sent by the `HeaderViewModel` after a login operation has completed, and both those other viewmodels might register handlers for it. For instance, `ConversationsListViewModel` will load the list of conversations for the new user, and `ConversationViewModel` will just close the current conversation, if one is present.
46
51
47
-
The `Messenger` class takes care of delivering messages to all the registered recipients. Note that a recipient can subscribe to messages of a specific type. Note that inherited message types are not registered in the default `Messenger` implementation.
52
+
The `IMessenger` instance takes care of delivering messages to all the registered recipients. Note that a recipient can subscribe to messages of a specific type. Note that inherited message types are not registered in the default `IMessenger` implementations provided by the MVVM Toolkit.
48
53
49
54
When a recipient is not needed anymore, you should unregister it so that it will stop receiving messages. You can unregister either by message type, by registration token, or by recipient:
> The `Messenger` implementation uses strong references to track the registered recipients. This is done for performance reasons, and it means that each registered recipient should manually be unregistered to avoid memory leaks. That is, as long as a recipient is registered, the `Messenger` instance in use will keep an active reference to it, which will prevent the garbage collector from being able to collect that instance. You can either handle this manually, or you can inherit from [`ObservableRecipient`](ObservableRecipient.md), which by default automatically takes care of removing all the message registrations for recipient when it is deactivated (see docs on `ObservableRecipient` for more info about this).
68
+
> As mentioned before, this is not strictly necessary when using the `WeakReferenceMessenger` type, as it uses weak references to track recipients, meaning that unused recipients will still be eligible for garbage collection even though they still have active message handlers. It is still good practice to unsubscribe them though, to improve performances. On the other hand, the `StrongReferenceMessenger` implementation uses strong references to track the registered recipients. This is done for performance reasons, and it means that each registered recipient should manually be unregistered to avoid memory leaks. That is, as long as a recipient is registered, the `StrongReferenceMessenger` instance in use will keep an active reference to it, which will prevent the garbage collector from being able to collect that instance. You can either handle this manually, or you can inherit from `ObservableRecipient`, which by default automatically takes care of removing all the message registrations for recipient when it is deactivated (see docs on `ObservableRecipient` for more info about this).
69
+
70
+
It is also possible to use the `IRecipient<TMessage>` interface to register message handlers. In this case, each recipient will need to implement the interface for a given message type, and provide a `Receive(TMessage)` method that will be invoke when receiving messages, like so:
The `RequestMessage<T>` class includes an implicit converter that makes the conversion from a `LoggedInUserRequestMessage` to its contained `User` object possible. This will also check that a response has been received for the message, and throw an exception if that's not the case. It is also possible to send request messages without this mandatory response guarantee: just store the returned message in a local variable, and then manually check whether a response value is available or not. Doing so will not trigger the automatic exception if a response is not received when the `Send` method returns.
@@ -94,13 +124,13 @@ public class LoggedInUserRequestMessage : AsyncRequestMessage<User>
0 commit comments