Messenger

接口 IMessenger 是可用于在不同对象之间交换消息的类型协定。 这可用于将应用程序的不同模块分离,而无需保留对所引用类型的强引用。 还可以将消息发送到特定通道,由令牌唯一标识,并在应用程序的不同部分中具有不同的信使。 MVVM Toolkit提供两种现装实现:WeakReferenceMessengerStrongReferenceMessenger者在内部使用弱引用,为收件人提供自动内存管理,而后者使用强引用,并且要求开发人员在不再需要接收者时手动取消订阅收件人, (有关如何取消注册消息处理程序的更多详细信息,请参阅以下) ,但换而言,这样可以提高性能和少得多的内存 使用。

平台 API:IMessenger、、、WeakReferenceMessengerIRecipient<TMessage>StrongReferenceMessengerObservableRecipientMessageHandler<TRecipient, TMessage>RequestMessage<T>AsyncRequestMessage<T>CollectionRequestMessage<T>、 。 AsyncCollectionRequestMessage<T>

工作原理

实现 IMessenger 的类型负责维护接收方与相对消息处理程序) 的邮件 (接收方之间的链接。 任何对象都可以使用消息处理程序将给定邮件类型注册为收件人,每当 IMessenger 该实例用于发送该类型的消息时,都会调用该对象。 还可以通过特定通信通道发送消息, (唯一令牌) 标识的每个通道,以便多个模块可以交换同一类型的消息,而不会造成冲突。 在没有令牌的情况下发送的消息使用默认共享通道。

可通过两种方式执行消息注册:通过 IRecipient<TMessage> 接口或使用充当消息处理程序的 MessageHandler<TRecipient, TMessage> 委托。 第一个允许使用对扩展的单个调用注册所有处理程序,该扩展 RegisterAll 会自动注册所有声明的消息处理程序的收件人,而后者在需要更多灵活性或想要将简单的 lambda 表达式用作消息处理程序时非常有用。

StrongReferenceMessenger同时WeakReferenceMessenger公开一个Default属性,该属性提供内置于包中的线程安全实现。 如果需要,还可以创建多个信使实例,例如,如果将另一个信使实例注入到应用的不同模块中, (应用的不同模块,则在同一进程中运行的多个窗口) 。

注意

由于类型WeakReferenceMessenger更易于使用,并且与库中信使类型MvvmLight的行为匹配,因此它是 MVVM Toolkit中类型使用ObservableRecipient的默认类型。 通过将实例传递给该类的构造函数,仍可使用该 StrongReferenceType 实例。

发送和接收消息

考虑以下情况:

// Create a message
public class LoggedInUserChangedMessage : ValueChangedMessage<User>
{
    public LoggedInUserChangedMessage(User user) : base(user)
    {        
    }
}

// Register a message in some module
WeakReferenceMessenger.Default.Register<LoggedInUserChangedMessage>(this, (r, m) =>
{
    // Handle the message here, with r being the recipient and m being the
    // input message. Using the recipient passed as input makes it so that
    // the lambda expression doesn't capture "this", improving performance.
});

// Send a message from some other module
WeakReferenceMessenger.Default.Send(new LoggedInUserChangedMessage(user));

假设此消息类型在简单的消息应用程序中使用,该应用程序显示一个标头,其中包含当前记录的用户的用户名和配置文件图像、一个包含对话列表的面板,以及另一个包含当前对话消息的面板(如果已选中)。 假设这三个部分分别受这些HeaderViewModelConversationsListViewModel部分和ConversationViewModel类型支持。 在此方案中,登录 LoggedInUserChangedMessage 操作完成后可能会发送 HeaderViewModel 消息,并且这两个其他 viewmodel 都可以为其注册处理程序。 例如, ConversationsListViewModel 将加载新用户的会话列表,如果存在会话,将 ConversationViewModel 仅关闭当前对话。

IMessenger 实例负责向所有已注册的收件人传送邮件。 请注意,收件人可以订阅特定类型的消息。 请注意,继承的消息类型未在 MVVM Toolkit提供的默认IMessenger实现中注册。

不再需要收件人时,应取消注册,以便停止接收邮件。 可以通过邮件类型、注册令牌或收件人取消注册:

// Unregisters the recipient from a message type
WeakReferenceMessenger.Default.Unregister<LoggedInUserChangedMessage>(this);

// Unregisters the recipient from a message type in a specified channel
WeakReferenceMessenger.Default.Unregister<LoggedInUserChangedMessage, int>(this, 42);

// Unregister the recipient from all messages, across all channels
WeakReferenceMessenger.Default.UnregisterAll(this);

警告

如前所述,在使用类型时 WeakReferenceMessenger ,这不是绝对必要的,因为它使用弱引用来跟踪收件人,这意味着未使用的收件人仍将有资格进行垃圾回收,即使它们仍然具有活动邮件处理程序。 不过,取消订阅它们还是不错的做法,以提高性能。 另一方面, StrongReferenceMessenger 实现使用强引用来跟踪已注册的收件人。 这是出于性能原因完成的,这意味着应手动取消注册每个已注册收件人以避免内存泄漏。 也就是说,只要注册收件人,正在使用的 StrongReferenceMessenger 实例就会保留对其的活动引用,这将阻止垃圾回收器能够收集该实例。 可以手动处理此内容,也可以从中 ObservableRecipient继承,默认情况下,在停用收件人的所有邮件注册时会自动将其删除, (查看 ObservableRecipient 有关此) 的详细信息的文档。

也可以使用 IRecipient<TMessage> 接口注册消息处理程序。 在这种情况下,每个收件人都需要为给定邮件类型实现接口,并提供 Receive(TMessage) 在接收消息时将调用的方法,如下所示:

// Create a message
public class MyRecipient : IRecipient<LoggedInUserChangedMessage>
{
    public void Receive(LoggedInUserChangedMessage message)
    {
        // Handle the message here...   
    }
}

// Register that specific message...
WeakReferenceMessenger.Default.Register<LoggedInUserChangedMessage>(this);

// ...or alternatively, register all declared handlers
WeakReferenceMessenger.Default.RegisterAll(this);

// Send a message from some other module
WeakReferenceMessenger.Default.Send(new LoggedInUserChangedMessage(user));

使用请求消息

信使实例的另一个有用功能是,它们还可用于将值从模块请求到另一个模块。 为此,包包含一个基 RequestMessage<T> 类,可以使用该基类,如下所示:

// Create a message
public class LoggedInUserRequestMessage : RequestMessage<User>
{
}

// Register the receiver in a module
WeakReferenceMessenger.Default.Register<MyViewModel, LoggedInUserRequestMessage>(this, (r, m) =>
{
    // Assume that "CurrentUser" is a private member in our viewmodel.
    // As before, we're accessing it through the recipient passed as
    // input to the handler, to avoid capturing "this" in the delegate.
    m.Reply(r.CurrentUser);
});

// Request the value from another module
User user = WeakReferenceMessenger.Default.Send<LoggedInUserRequestMessage>();

RequestMessage<T> 类包含一个隐式转换器,使从 LoggedInUserRequestMessage 其包含 User 的对象进行转换成为可能。 这还会检查是否已收到消息的响应,如果不是这种情况,则引发异常。 还可以发送请求消息而不提供此强制响应保证:只需将返回的消息存储在本地变量中,然后手动检查响应值是否可用。 如果方法返回时 Send 未收到响应,则这样做不会触发自动异常。

同一命名空间还包括其他方案的基请求消息:AsyncRequestMessage<T>CollectionRequestMessage<T>AsyncCollectionRequestMessage<T>。 下面介绍如何使用异步请求消息:

// Create a message
public class LoggedInUserRequestMessage : AsyncRequestMessage<User>
{
}

// Register the receiver in a module
WeakReferenceMessenger.Default.Register<MyViewModel, LoggedInUserRequestMessage>(this, (r, m) =>
{
    m.Reply(r.GetCurrentUserAsync()); // We're replying with a Task<User>
});

// Request the value from another module (we can directly await on the request)
User user = await WeakReferenceMessenger.Default.Send<LoggedInUserRequestMessage>();

示例