常用消息类型 Messages
工具包为我们提供了几个常用的消息类型,包括:
ValueChangedMessagePropertyChangedMessageRequestMessage
这几种消息类型有的拥有一些特殊的功能,下面将会详细介绍。如果我们希望对消息类型进行定制或扩展,那么可以继承这些消息类型,并实现自己的消息类型。
自定义 Message
工具包中的 IMessenger 接口约定的 Send、Register 等泛型方法,并没有要求消息的种类满足任何条件(比如继承自某个基类),所以完全可以自由地实现任何消息类型。
比如这里,我们用 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
AsyncRequestMessage 是 RequestMessage 的异步版本。但是它可能稍微有一点反常识。在使用它时,与其说是接收方在异步地处理消息并返回结果,不如说它直接将一个异步任务丢给了发送者,并让发送者自己在接到异步任务后开始等待任务的完成,并最终拿到结果。
为什么要这样设计?
这样设计其实是有原因的:IMessenger 的 Register 方法中传入的回调是一个 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。
这个类通常配合 ObservableRecipient 的 Broadcast 方法使用,详见 相关章节。