MAUI新生2.4-数据绑定和MVVM:MVVM的消息机制
当两个对象之间需要建立松耦合关系时,特别适合使用消息机制。如ViewModel需要控制View进行某些操作时,如弹出对话框、播放动画,由于ViewModel对View是无感的,我们不能在ViewModel中去操作View,否则会造成ViewModel对View的依赖。另外,ViewModel层之间、以及View页面之间,也可能需要通过消息传递来完成一些交互。CommunityToolkit.Mvvm提供了比较完善的消息机制,以下内容主要介绍它的使用。使用前,需安装nuget包:CommunityToolkit.Mvvm。
一、基本过程:如下图所示,一个消息的收发,或者叫订阅与发布,主要有三个过程:
1、创建信使和消息类型,信使的作用:一是起到消息发布者与订阅者的桥梁作用;二是携带消息。在代码层面,信使是派生自ValueChangedMessage<T>或RequestMessage<T>的普通类,其中泛型T为消息的类型。
2、订阅消息(Register),消息订阅者通过【WeakReferenceMessenger.Default.Register<T>(this,(r,m)=>{});】订阅消息,其中泛型T为信使类,只要这个信使类被发布,它就能收到通知和消息。【(r,m)=>{}】为订阅者收到通知后的执行逻辑,是一个Lambda函数,也有叫事件处理者Handler。其中参数 r 指消息订阅所在的这个对象,通过this传入,通过 r 可以在Lambda函数中引用其它成员;参数 m 就是信使携带的消息对象。
3、发布消息(Send),消息发布者通过【WeakReferenceMessenger.Default.Send(new一个信息类对象)】发布消息。
二、在何处执行订阅和发布:如上图,虽然知道了订阅和发布的代码,但它们应该在何处执行?
1、在哪里执行消息订阅:View和ViewModel中,都可以订阅消息。其中View在构造函数中订阅。而ViewModel,Mvvm为我们提供了ObservableRecipient基类(ObservableRecipient派生自ObservableObject),继承它后,有两个重写方法OnActivated和OnDeactivated,消息订阅写在OnActivated方法中,消息注销写在OnDeactivated方法中。
2、在哪里发布消息:任何可执行方法中,都可以发布消息。在Mvvm开发模式中,我们一般在命令中发布消息。
三、案例一:ViewModel通过一个命令发布消息给View,View收到消息通知后,通过弹窗显示消息
1、创建信使,消息类型为string,HiMessenger.cs
//信使类为HiMessenger,消息类型为string public class HiMessenger: ValueChangedMessage<string> { public HiMessenger(string value):base(value) //创建信使对象时,通过构造函数初始化消息 { } }
2、ViewModel中发布消息,MainPageViewModel.cs
public partial class MainPageViewModel:ObservableRecipient { protected override void OnActivated() { base.OnActivated();//在OnActivated方法中订阅消息,本例中不需要 } [RelayCommand] private void SendHiMessage() { WeakReferenceMessenger.Default.Send(new HiMessenger("Hi,I'm functionMC.I come from China."));//发布消息 } }
3、View的后台代码订阅消息并在回调中弹窗显示消息,View的XAML中绑定SendHiMessageCommand命令,MainPage.xaml和MainPage.xaml.cs
//前台代码MainPage.xaml <ContentPage ...... BindingContext="{Binding MainPageViewModel,Source={StaticResource ServiceLocator}}"> <VerticalStackLayout> <Button Text="发送信息" Command="{Binding SendHiMessageCommand}"></Button> </VerticalStackLayout> </ContentPage> //后台代码MainPage.xaml.cs public partial class MainPage : ContentPage { public MainPage() { InitializeComponent(); //订阅消息,回调中执行ShowHiMessage函数,并传入接收到的消息 WeakReferenceMessenger.Default.Register<HiMessenger>(this, async (r,m) => { await ShowHiMessage(m.Value); }); } public async Task ShowHiMessage(string message) { await DisplayAlert("Alert",message,"OK");//弹窗显示消息 } }
四、案例二:AddName页面,添加姓名;NameList页面,显示姓名列表。AddNameViewModel通过消息机制,将绑定属性Name发送给NameListViewModel,NameListViewModel收到信息后,添加到NameList集合。
//①创建信使类,NameMessenger.cs===================================================================== public class NameMessenger : ValueChangedMessage<string> { public NameMessenger(string value) : base(value) { } }
//②订阅信息,NameListViewModel.cs=================================================================== public partial class NameListViewModel:ObservableRecipient { protected override void OnActivated() { //订阅信使NameMessenger,并将收到的信息添加到NameList集合中 WeakReferenceMessenger.Default.Register<NameListViewModel, NameMessenger>(this, (r,m) => { r.NameList.Add(m.Value); }); }
//绑定姓名列表页面的ListView控件显示姓名列表 [ObservableProperty] private ObservableCollection<string> nameList; }
//③发布信息,AddNameViewModel.cs===================================================================== public partial class AddNameViewModel:ObservableRecipient { //绑定View层的输入姓名 [ObservableProperty] private string name; //绑定View层的按钮执行添加命令 [RelayCommand] private void AddName() { //在命令中,发送信使NameMessenger,携带信息Name WeakReferenceMessenger.Default.Send(new NameMessenger(Name)); } } //View层略
五、其它知识点
1、传递的消息类型可以用复杂类型吗?可以,直接在信息类的泛型参数<T>中指定
2、消息发布者发布消息后,可以收到消息订阅者传回的信息吗?可以!如代码所示:
//以下信使类、消息类、ViewMode类,均为案例,按实际需求替换 //信使类继承自RequestMessage<T> public class CurrentUserMessenger : RequestMessage<User>{} //消息订阅者使用m.Reply返回信息 WeakReferenceMessenger.Default.Register<UserSenderViewModel,CurrentUserMessenger>(this,(r,m)=>m.Reply(r.UserName)); //消息发布者接收返回值 UserName = WeakReferenceMessenger.Default.Send<CurrentUserMessenger>();
3、WeakRefereceMessenger和StrongReferenceMessenger的区别?
Toolkit.Mvvm除了提供WeakRefereceMessenger(消息中心)之外,还提供了StrongReferenceMessenger。两者区别在于使用Weak...,系统会自动进行垃圾回收,防止内存泄漏;而Strong...需要手动注销消息订阅者,由于不需要启用自动回收机制,所以性能会好一点,但如果忘记注销,就容易引起内存泄漏,使用开发中,推荐使用Weak...。注销方式如下:
StrongReferenceMessenger.Default.Unregister<LoggedInUserChangedMessage>(this);//注销当前对象订阅的指定消息 StrongReferenceMessenger.Default.UnregisterAll(this);//注销当前对象订阅的所有消息
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!