跳转至

常用消息类型 Messages

工具包为我们提供了几个常用的消息类型,包括:

  • ValueChangedMessage
  • PropertyChangedMessage
  • RequestMessage

这几种消息类型有的拥有一些特殊的功能,下面将会详细介绍。如果我们希望对消息类型进行定制或扩展,那么可以继承这些消息类型,并实现自己的消息类型。

自定义 Message

工具包中的 IMessenger 接口约定的 SendRegister 等泛型方法,并没有要求消息的种类满足任何条件(比如继承自某个基类),所以完全可以自由地实现任何消息类型。

比如这里,我们用 C# 9.0 为我们带来的 record 类型快速声明一个消息类型,并使用:

// 声明一个消息类型
public record MyMessage(string Content);

// 发送消息
WeakReferenceMessenger.Default.Send(new MyMessage("Hello World!"));

// 接收消息
WeakReferenceMessenger.Default.Register<MyMessage>(this, message =>
{
    Console.WriteLine(message.Content);
});

ValueChangedMessage

ValueChangedMessage 是一个最基本的消息类型,它包含一个 Value 属性,用于存储消息的值。如果希望发送一个用于通知某个值发生变化的消息,可以使用 ValueChangedMessage

此外,还可以继承这个类,从而实现自己的消息类型。比如:

public class MyMessage : ValueChangedMessage<string>
{
    public MyMessage(string value) : base(value)
    {
    }
}

// 发送消息
WeakReferenceMessenger.Default.Send(new MyMessage("Hello World!"));

// 接收消息
WeakReferenceMessenger.Default.Register<MyMessage>(this, message =>
{
    Console.WriteLine(message.Value);
});

没有接收者?

对于一个不需要回复的消息(不是 RequestMessage 或其子类),如果没有任何接收者,那么 Send 方法将会正常运行,且不会有任何效果。

RequestMessage

这是一个特殊的消息类型。如果 Send 方法发送的是这个类(或它的子类),那么 Send 方法将拥有一个返回值,这个返回值就是消息的接收者回复的消息。此时,消息的接收者也能够通过消息上的 Reply 方法回复消息的发送者。比如:

// 注册消息接收者
WeakReferenceMessenger.Default.Register<MyRequestMessage>(
    new object(),
    (_, m) =>
    {
        // 收到消息后,会进行简单的判断,并回复消息
        if (m.Content == "Nice to meet you!")
            m.Reply("Nice to meet you, too!");
        else
            m.Reply("Yes?");
    }
);

// 发送消息并查看回复的消息内容
var res1 = WeakReferenceMessenger.Default.Send(new MyRequestMessage("Hello!"));
Console.WriteLine(res1.Response);

var res2 = WeakReferenceMessenger.Default.Send(new MyRequestMessage("Nice to meet you!"));
Console.WriteLine(res2.Response);

// 声明一个自定义消息类型
public class MyRequestMessage : RequestMessage<string>
{
    public string Content { get; init; }

    public MyRequestMessage(string content)
    {
        Content = content;
    }
}

然后就能在控制台看到这样的输出内容:

Yes?
Nice to meet you, too!

没有回复者?

由于在发送 RequestMessage 时,消息的接收者需要等待回复消息,所以如果此时没有接受者,那么可能会出现问题。具体来说,单纯调用 Send 不会报错,但是如果试图访问 response.Response则会抛出异常。原因是并没有任何接收者提供了回复。

有多个回复者?

如果有多个接收者回复了消息,那么将会报错。此时正确的做法是,可以从 RequestMessage 上的 HasReceivedResponse 属性判断是否已经有接收者回复了消息。如果已经有接收者回复了消息,那么这个属性的值将会为 true

AsyncRequestMessage

AsyncRequestMessageRequestMessage 的异步版本。但是它可能稍微有一点反常识。在使用它时,与其说是接收方在异步地处理消息并返回结果,不如说它直接将一个异步任务丢给了发送者,并让发送者自己在接到异步任务后开始等待任务的完成,并最终拿到结果。

为什么要这样设计?

这样设计其实是有原因的:IMessengerRegister 方法中传入的回调是一个 void 类型的,也就是说如果我们想在接收者这边进行异步处理,我们只能给它传入一个 async void 的回调。这是很不理想的方式。

我们看一个简单的例子:

// 接收方
public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();

        WeakReferenceMessenger.Default.Register<AsyncRequestMessage<string>>(this, (_, m) =>
        {
            // 这里我们回复消息时,其实异步任务只是刚刚开始,还没有完成
            m.Reply(GetStringAsync());
        });
    }

    // 模拟一个需要耗时一段时间才能得到结果的任务
    private async Task<string> GetStringAsync()
    {
        await Task.Delay(2000);
        return "hello, world!";
    }
}

// 发送方
partial class MainViewModel : ObservableObject
{
    [RelayCommand]
    private async Task SendMessageAsync()
    {
        var request = WeakReferenceMessenger.Default.Send<AsyncRequestMessage<string>>();
        var response = await request.Response; // 这里的 Response 属性是一个 Task<string>
    }
}

通过这样的方式,我们就可以实现异步地接收消息回复了。

PropertyChangedMessage

PropertyChangedMessage 是一个用于通知属性发生变化的消息类型,它包含一个 PropertyName 属性,用于存储属性的名称。如果希望发送一个用于通知某个属性发生变化的消息,可以使用 PropertyChangedMessage

这个类通常配合 ObservableRecipientBroadcast 方法使用,详见 相关章节

评论