CommunityToolkit.Mvvm学习笔记(4)——Messenger

这篇具有很好参考价值的文章主要介绍了CommunityToolkit.Mvvm学习笔记(4)——Messenger。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

Messenger概述

如果你对WPF有一定了解,你应该知道WPF中的命令是一个实现了ICommand接口的类。同样本文虽然标题是Messenger,但也要从IMessenger接口说起。至于Messenger的中文名,我觉得就叫它的直译“信使”好了,毕竟传递消息就是信使的能力嘛。

1 IMessenger接口

命名空间:Microsoft.Toolkit.Mvvm.Messaging
程序集:Microsoft.Toolkit.Mvvm.dll
包:Microsoft.Toolkit.Mvvm

IMessenger接口提供了不同对象间交换消息的类应该具有的能力,这对解耦程序的不同模块非常有用,而不必持有引用类型的强引用

强引用:
当应用程序的代码能够访问到程序正在使用的对象时,GC(.NET平台的垃圾收集器)就不能回收该对象。
就称程序对该对象进行了强引用。
强引用和这有什么关系呢?
如果结合“解耦程序”来理解就会明了许多。
不关心代码能不能访问到该对象,我一样可以交换消息。

你也可以将消息发送到指定通道(通过令牌唯一标识,uniquely identified by a token),让不同的信使呆在程序的不同部分。

要使用 IMessenger 功能,首先定义一个消息类,像这样:

// 定义一个登陆完成的消息
//(这里为了说明步骤而不是谈具体实现,所以只是定义了一个普通的class,
// 实际上它应该继承并实现IMessenger接口,不过MVVM工具包为你提供现成的类,后面会提到)
public sealed class LoginCompletedMessage { }
// sealed关键词会防止其它类继承该类
// 至于为什么加这个关键词,因为例子里加了

接着,为该消息注册一个接收者(Recipient):

Messenger.Default.Register<MyRecipientType, LoginCompletedMessage>(this, (r, m) =>
{
    // Handle the message here...
});

这里的消息处理器是一个有两个参数的lambda表达式:接收者(r,recipient)和消息(m,message)。这么做是为了避免分配闭包(这个词可以百度了解一下,在lambda表达式中很常见),如果表达式捕获了当前实例,就会生成闭包。将接收者作为参数,是为了能在处理程序中直接访问它,而不需要手动进行类型转换。

xx_handler(object sender, Args e)
{
	// 这类用法大家一定不陌生,需要你手动将object转换类型
	(sender as xxType).Method();
}

将接收者作为参数能使代码冗余更少,更可靠,因为所有的检查在构建时就完成了。

如果处理器(handler)定义在与接收者相同的类中,那它也可以直接访问私有成员。这允许消息处理器是一个静态方法,这使得C#编译器能执行一系列额外的内存优化(例如缓存委托,避免不必要的内存分配)。最后,在需要时发送消息,就像这样:

Messenger.Default.Send<LoginCompletedMessage>();

此外,若当前作用域内有可用的带有正确签名的方法,方法组语法还可用于指定在接收消息时调用的消息处理器。这有助于使注册和处理逻辑分离。在上个例子的基础上,考虑一个带有以下方法的类:

private static void Receive(MyRecipientType recipient, LoginCompletedMessage message)
{
    // Handle the message there
}

以下代码进行注册:

Messenger.Default.Register(this, Receive);

C#编译器自动将表达式转化为一个与 Register<TRecipient,TMessage>(IMessenger, TRecipient, MessageHandler<TRecipient,TMessage>) 兼容的 MessageHandler<TRecipient,TMessage> 实例。如果该方法有多个重载可用,每个重载处理着不同的消息类型:C#编译器将自动根据当前消息类选择一个合适的。也可以显式注册使用 IRecipient<TMessage> 接口的消息处理器。若这样做,接收者只需要实现该接口,然后调用 RegisterAll(IMessenger, Object) 扩展,该拓展将会自动注册由接收者类型所声明的所有处理器。当然,只有一个处理器也支持。

下面是IMessenger接口,
CommunityToolkit.Mvvm学习笔记(4)——Messenger
IMessenger有两个派生类:

  • Microsoft.Toolkit.Mvvm.Messaging.StrongReferenceMessenger
  • Microsoft.Toolkit.Mvvm.Messaging.WeakReferenceMessenger

这两个类是MVVM工具包提供的两种即用的实现(就是已经给你实现好了IMessenger),

  • 前者在内部使用的是弱引用,为接收者提供自动内存管理
  • 后者使用强引用,要求开发者(在不再需要接收者时)手动取消接收者的订阅。但作为交换,它提供了更好的性能和更少的内存使用。

相关的平台API:IMessenger, WeakReferenceMessenger, StrongReferenceMessenger, IRecipient<TMessage>, MessageHandler<TRecipient, TMessage>, ObservableRecipient, RequestMessage<T>, AsyncRequestMessage<T>, CollectionRequestMessage<T>, AsyncCollectionRequestMessage<T>.

2 信使的工作原理

实现了 IMessenger 接口的类负责维护消息接收者(recipient)和注册消息类之间的链接(link),以及相关的消息处理器(handlers)。任何对象都可以使用消息处理器注册为指定消息类的接收者,每当使用 IMessenger 实例发送该类的消息时,消息处理器会被调用。也可以通过特定通信通道(由唯一令牌标识)发送消息,这样多个模块间可以交换相同类型的消息,而不会引起冲突。不使用令牌发送的消息使用默认共享通道。

有两种注册消息的方式:
一种是使用 IRecipient<TMessage> 接口,另一种是用 MessageHandler<TRecipient, TMessage> 委托来作为消息处理器。
CommunityToolkit.Mvvm学习笔记(4)——Messenger
CommunityToolkit.Mvvm学习笔记(4)——Messenger
第一种方法,允许你用 RegisterAll 扩展方法来注册所有处理器,它会自动注册所有声明消息处理器的接收者。而后者在你需要更灵活或者简单的lambda表达式作为消息处理器时更适用。

WeakReferenceMessengerStrongReferenceMessenger 均暴露了 Default 属性,该属性提供了一个内置于包中的线程安全实现。如果需要,也可以创建多个messenger实例,例如,将不同的实例用DI服务提供者(Service provider)注入到程序不同的模块中(如,多个窗口运行在同一个进程中时)。

注意:
由于 WeakReferenceMessenger 用起来更简单,并与 MvvmLight 库中的messenger的行为更匹配,
所以它是MVVM Toolkit中 ObservableRecipient 默认的使用类型。
通过传递实例给该类的构造函数,StrongReferenceMessenger 也仍然可用。

3 收发消息

考虑以下代码:

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

假设在一个简单的消息通信程序(聊天软件)中使用该消息类,该程序显示一个带有当前登录用户的用户名和简介图像的标题栏,一个带有聊天列表的面板,以及另一个带有当前聊天消息的面板(选中了其中一个)。假设这三个部分分别由 HeaderViewModel , ConversationsListViewModelConversationViewModel 支持。在该场景中,登录操作完成后, 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 时,上面的注销操作不是严格需要的,因为使用弱引用来追踪接收者意味着不用的接收者即使仍然有激活的消息处理程序,它们仍会被GC清理。不过,取消订阅它们仍然是一个好的做法,这可以提高性能。

另一方面,StrongReferenceMessenger 实现使用了强引用来跟踪注册的接收者。这样做是出于性能考虑,这意味着每个注册的接收者应该手动被注销以避免内存泄漏。也就是说,只要注册了一个接收者,使用中StrongReferenceMessenger实例就会保活对它的引用,这将防止GC回收该实例。你可以手动处理它,也可以从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));

4 使用请求消息

messenger实例另一个有用的特性是,它可以用于从一个模块向另一个模块请求值。要做到这点,该包包含了一个 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> 。下面是一个使用异步请求消息的例子:文章来源地址https://www.toymoban.com/news/detail-450208.html

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

到了这里,关于CommunityToolkit.Mvvm学习笔记(4)——Messenger的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • CommunityToolkit.Mvvm 加速 MVVM 开发

    为保文字描述的准确性,文章中的文字描述信息多为引用官方。 依赖注入部分按照官方文档的步骤,但是替换成了我自己写的WPF的内容,因为官方给的是UWP这块不熟悉怕出错就替换了。 引用 Microsoft Document 中的介绍 包 CommunityToolkit.Mvvm (又名 MVVM 工具包,以前名为 Microsoft.To

    2024年02月03日
    浏览(40)
  • WPF CommunityToolkit.Mvvm

    CommunityToolkit.Mvvm(以下简称Toolkit)是WPF最有名的两个框架,一个是Prism,另一个就是Toolkit。 Prism可以看我的Prism详解 WPF Prims框架详解 Toolkit 官方文档 用 CommunityToolkit.Mvvm 加速 MVVM 开发流程 Toolkit简单复写了我们常用的两个方法 一个是 SetProperty,一个是RelayCommand SetProperty,通知

    2024年02月12日
    浏览(41)
  • CommunityToolkit.Mvvm----配置

    一、介绍: CommunityToolkit.Mvvm 包(又名 MVVM 工具包,以前称为 Microsoft.Toolkit.Mvvm)是一个现代、快速和模块化的 MVVM 库。 它是 .NET 社区工具包的一部分,围绕以下原则生成: 独立于平台和运行时 - .NET Standard 2.0、.NET Standard 2.1 和 .NET 6🚀(与 UI 框架无关) 易于选取和使用 - 对

    2024年04月17日
    浏览(82)
  • CommunityToolkit.Mvvm系列文章导航

    包  CommunityToolkit.Mvvm  (又名 MVVM 工具包,以前名为  Microsoft.Toolkit.Mvvm ) 是一个现代、快速且模块化的 MVVM 库。 它是 .NET 社区工具包的一部分,围绕以下原则构建: 平台和运行时独立  -  .NET Standard 2.0 、  .NET Standard 2.1  和  .NET 6 🚀 (UI Framework 不可知) 易于选取和使用  -

    2023年04月15日
    浏览(41)
  • CommunityToolkit.Mvvm8.1 MVVM工具包安装引用指南(1)

      本系列文章导航 https://www.cnblogs.com/aierong/category/2297596.html     0.说明 CommunityToolkit.Mvvm包(又名MVVM 工具包,以前名为 Microsoft.Toolkit.Mvvm)是一个现代、快速且模块化的 MVVM 库。 它支持:.NET Standard 2.0、 .NET Standard 2.1 和 .NET 6(UI Framework 不支持)   文档地址: https://learn.microsoft.com/z

    2023年04月09日
    浏览(52)
  • wpf CommunityToolkit.Mvvm8.1 MVVM工具包安装引用指南

      本系列文章导航 https://www.cnblogs.com/aierong/category/2297596.html     0.说明 CommunityToolkit.Mvvm包(又名MVVM 工具包,以前名为 Microsoft.Toolkit.Mvvm)是一个现代、快速且模块化的 MVVM 库。 它支持:.NET Standard 2.0、 .NET Standard 2.1 和 .NET 6(UI Framework 不支持)   文档地址: https://learn.microsoft.com/z

    2023年04月08日
    浏览(41)
  • CommunityToolkit.Mvvm8.1 消息通知(4)

    本系列文章导航 https://www.cnblogs.com/aierong/p/17300066.html https://github.com/aierong/WpfDemo (自我Demo地址)   希望提到的知识对您有所提示,同时欢迎交流和指正 作者:aierong 出处:https://www.cnblogs.com/aierong   为了应用程序的不同模块分离,减少模块之间引用,CommunityToolkit.Mvvm提供了消息通知功

    2023年04月14日
    浏览(54)
  • [.NET/WPF] CommunityToolkit.Mvvm 异步指令

    我们在开发中, 经常会有这样的需求: 点击按钮后, 进行一些耗时的工作 工作进行时, 按钮不可再次被点击 工作进行时, 会显示进度条, 或者 “加载中” 的动画 CommunityToolkit.Mvvm 中的 RelayCommand 除了支持最简单的同步方法, 还支持以 Task 作为返回值的异步方法, 当我们为这样的异

    2024年02月12日
    浏览(44)
  • VisualStudio[WPF/.NET]基于CommunityToolkit.Mvvm架构开发

    项目模板选择如下: 暂时随机填一个目标框架,待会改: 双击“解决方案资源管理器”中项目CU-APP, 打开项目工程文件CU-APP.csproj, 修改目标框架 TargetFramework 为.net framework 4.7.2 ;  C#语言版本 LangVersion 为  9.0  \\\"工具\\\" - \\\"NuGet包管理器\\\",“管理解决方案的NuGet程序包(N)”,  或者,

    2024年02月07日
    浏览(40)
  • CommunityToolkit.Mvvm8.1 viewmodel使用-旧式写法(2)

      本系列文章导航 https://www.cnblogs.com/aierong/p/17300066.html https://github.com/aierong/WpfDemo (自我Demo地址)     CommunityToolkit.Mvvm8.1有一个重大更新的功能:源生成器功能,它极大简化我们的mvvm代码 但是本篇先总结一下原写法,下篇再总结源生成器功能   必须继承:ObservableObject   几个关键点

    2023年04月09日
    浏览(31)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包