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);//注销当前对象订阅的所有消息

 

posted @ 2022-12-02 00:14  functionMC  阅读(1251)  评论(0编辑  收藏  举报