本系列主要介绍微软社区工具包CommunityToolKit.Mvvm,是本人在观看B站UP主十月的寒流视频时的个人总结,真心感谢!学习C#的同学强力建议关注此UP,并观看其全部视频。
如何使用 CommunityToolkit.Mvvm 中的 Messenger 来进行 ViewModel 之间的通信_哔哩哔哩_bilibili
语言:C#
IDE:LINQPad7.0,Microsoft Visual Studio Community 2022
框架:WPF,.net 8.0
零、LINQPad
LINQPad是一个轻量级的C# 和 SQL 编写和测试工具,可用于代码片段的快速验证
下载链接:LINQPad v8.4.11 .NET开发操练场 - 『精品软件区』 - 吾爱破解 - LCG - LSG |安卓破解|病毒分析|www.52pojie.cn
一、定义和作用
Messenger的作用为搭建一个通道,为不同的ViewModel发送和接收消息。
二、自定义Messenger
再次感叹UP主的代码能力,膜拜!
var rec1 = new StringReceiver();
var rec2 = new NumberReceiver();
EventAggregator.Instance.Send(new StringMessage("hello world!"));
EventAggregator.Instance.Send(new StringMessage("goodby world!"));
EventAggregator.Instance.Send(new NumberMessage(123));
abstract class MessageReceiver<TMessage>
{
protected MessageReceiver()
{
EventAggregator.Instance.Register<TMessage>(this, Receive);
}
public abstract void Receive(TMessage m);
}
class StringReceiver : MessageReceiver<StringMessage>
{
public override void Receive(StringMessage message)
{
Console.WriteLine("String message received:" + message.ToString());
}
}
class NumberReceiver : MessageReceiver<NumberMessage>
{
public override void Receive(NumberMessage message)
{
Console.WriteLine("Number message received:" + message.ToString());
}
}
record StringMessage(string Message);
record NumberMessage(int Value);
class EventAggregator
{
public static readonly EventAggregator Instance = new EventAggregator();
record MessageReceiver(object Receiver, Action<object> Method);
private Dictionary<Type, List<MessageReceiver>> events = new Dictionary<Type, List<MessageReceiver>>();
public void Register<TMessage>(object receiver, Action<TMessage> method)
{
var type = typeof(TMessage);
if (!events.ContainsKey(type))
{
events[type] = new List<MessageReceiver>();
}
events[type].Add(item: new MessageReceiver(receiver, o => method((TMessage)o)));
}
public void Send<TMessage>(TMessage message)
{
var type = typeof(TMessage);
if (!events.ContainsKey(type))
{
events[type] = new();
}
foreach (var rec in events[type])
{
rec.Method.Invoke(message);
}
}
}
输出结果:
String message received:StringMessage { Message = hello world! }
String message received:StringMessage { Message = goodby world! }
Number message received:NumberMessage { Value = 123 }
三、使用Toolkit Messenger
ValueChangeMessage<T>可用于自定义类型的消息通知
PropertyChangedMessage<T>可用于属性更改时的消息通知
var rec = new ViewModel();
var rec2 = new ViewModel2();
rec2.Number = 10;
WeakReferenceMessenger.Default.Send(new ValueChangedMessage<string>("Hello world!"));
class ViewModel
{
public ViewModel()
{
WeakReferenceMessenger.Default.Register<ValueChangedMessage<string>>(this, Receive);
WeakReferenceMessenger.Default.Register<PropertyChangedMessage<int>>(this, Receive2);
}
private void Receive2(object recipient, PropertyChangedMessage<int> message)
{
message.Dump("Int message received");
}
private void Receive(object recipient, ValueChangedMessage<string> message)
{
message.Dump("String message received");
}
}
class ViewModel2 : ObservableObject
{
int number;
public int Number
{
get { return number; }
set
{
if (SetProperty(ref number, value))
{
WeakReferenceMessenger.Default.Send(new PropertyChangedMessage<int>(this, nameof(Number), default, value));
}
}
}
}
输出结果:
四、RequestMessage
RequestMessage带一个返回类型,类似回调,可回传一个值
var rec = new ReceiverViewModel();
var sdr = new SenderViewModel();
class ReceiverViewModel:ObservableObject,IRecipient<RequestMessage<string>>
{
public ReceiverViewModel()
{
WeakReferenceMessenger.Default.Register(this);
}
public void Receive(RequestMessage<string> message)
{
"Request message Received.".Dump();
message.Reply("Hello");
}
}
class SenderViewModel
{
public SenderViewModel()
{
var res = WeakReferenceMessenger.Default.Send(new RequestMessage<string>());
res.Response.Dump("Respon received");
}
}
运行结果:
Request message Received.
Respon received
Hello
五、Demo
实现效果为:
1.可输入Name,Class,Phone,点击Add New按钮后添加到DataGrid。
2.CheckBox可实现对Add New按钮属性IsEnabled的修改
3.在编辑Name/Class/Phone时,下方的TextBlock实时显示
完整代码见本文档附带的下载链接
六、总结
6.1 注册消息
6.1.1 方法:void Register<TMessage>(object recipient, MessageHandler<object, TMessage> handler)
反编译:
public static void Register<TMessage>(this IMessenger messenger, object recipient, MessageHandler<object, TMessage> handler) where TMessage : class
{
ArgumentNullException.ThrowIfNull(messenger, "messenger");
ArgumentNullException.ThrowIfNull(recipient, "recipient");
ArgumentNullException.ThrowIfNull(handler, "handler");
messenger.Register(recipient, default(Unit), handler);
}
使用举例:
var rec = new ReceiveViewModel();
WeakReferenceMessenger.Default.Send(new ValueChangedMessage<string>("Hello"));
class ReceiveViewModel
{
public ReceiveViewModel()
{
WeakReferenceMessenger.Default.Register<ValueChangedMessage<string>>(this, Receive);
}
void Receive(object recipient, ValueChangedMessage<string> message)
{
message.Dump($"{GetType().Name} messsage has receive");
}
}
运行结果:
6.1.2 接口:interface IRecipient<in TMessage> where TMessage : class
反编译:
public interface IRecipient<in TMessage> where TMessage : class
{
/// <summary>
/// Receives a given <typeparamref name="TMessage" /> message instance.
/// </summary>
/// <param name="message">The message being received.</param>
void Receive(TMessage message);
}
使用举例:
var rec = new ReceiveViewModel();
WeakReferenceMessenger.Default.Send(new ValueChangedMessage<string>("Hello"));
class ReceiveViewModel : IRecipient<ValueChangedMessage<string>>
{
public ReceiveViewModel()
{
WeakReferenceMessenger.Default.Register(this);
}
public void Receive(ValueChangedMessage<string> message)
{
message.Dump($"{GetType().Name} messsage has receive");
}
}
运行结果:
6.1.3 基类ObservableRecipient和接口 Recipient<in TMessage>
注意,需要在属性IsActive=true时才会接收到消息
使用举例:
var rec = new ReceiveViewModel(){IsActive = true};
WeakReferenceMessenger.Default.Send(new ValueChangedMessage<string>("Hello"));
class ReceiveViewModel : ObservableRecipient, IRecipient<ValueChangedMessage<string>>
{
public void Receive(ValueChangedMessage<string> message)
{
message.Dump($"{GetType().Name} messsage has receive");
}
}
运行结果:
6.1.4 基类ObservableRecipient和方法OnActivated
注意,需要在属性IsActive=true时才会接收到消息
使用举例
var rec = new ReceiveViewModel(){IsActive = true};
WeakReferenceMessenger.Default.Send(new ValueChangedMessage<string>("Hello"));
class ReceiveViewModel : ObservableRecipient
{
protected override void OnActivated()
{
Messenger.Register<ValueChangedMessage<string>>(this, Receive);
}
void Receive(object recipient, ValueChangedMessage<string> message)
{
message.Dump($"{GetType().Name} messsage has receive");
}
}
运行结果:
6.2 发送消息
6.2.1 Send<TMessage>(TMessage message)
只要注册类型相同,就可接受到发送的消息
反编译:
public static TMessage Send<TMessage>(this IMessenger messenger, TMessage message) where TMessage : class
{
ArgumentNullException.ThrowIfNull(messenger, "messenger");
ArgumentNullException.ThrowIfNull(message, "message");
return messenger.Send(message, default(Unit));
}
使用举例:
var rec1 = new ReceiverViewModel();
var rec2 = new ReceiverViewMode2();
WeakReferenceMessenger.Default.Send<string>("Hello");
class ReceiverViewModel : ObservableObject, IRecipient<string>
{
public ReceiverViewModel()
{
WeakReferenceMessenger.Default.Register(this);
}
public void Receive(string message)
{
message.Dump($"{GetType().Name} has received");
}
}
class ReceiverViewMode2 : ObservableObject, IRecipient<string>
{
public ReceiverViewMode2()
{
WeakReferenceMessenger.Default.Register(this);
}
public void Receive(string message)
{
message.Dump($"{GetType().Name} has received");
}
}
运行结果:
ReceiverViewModel has received
Hello
ReceiverViewMode2 has received
Hello
6.2.2 Send<TMessage>(TMessage message) where TMessage : class
只要注册类型相同,就可接受到发送的消息
反编译:
public static TMessage Send<TMessage>(this IMessenger messenger, TMessage message) where TMessage : class
{
ArgumentNullException.ThrowIfNull(messenger, "messenger");
ArgumentNullException.ThrowIfNull(message, "message");
return messenger.Send(message, default(Unit));
}
使用举例:
var rec1 = new ReceiverViewModel1();
var rec2 = new ReceiverViewModel2();
WeakReferenceMessenger.Default.Send(new ValueChangedMessage<int>(123));
WeakReferenceMessenger.Default.Send(new ValueChangedMessage<string>("Hello"));
class ReceiverViewModel1 : ObservableObject, IRecipient<ValueChangedMessage<string>>
{
public ReceiverViewModel1()
{
WeakReferenceMessenger.Default.Register(this);
}
public void Receive(ValueChangedMessage<string> message)
{
message.Dump($"{GetType().Name} has received");
}
}
class ReceiverViewModel2 : ObservableObject, IRecipient<ValueChangedMessage<string>>
{
public ReceiverViewModel2()
{
WeakReferenceMessenger.Default.Register(this);
}
public void Receive(ValueChangedMessage<string> message)
{
message.Dump($"{GetType().Name} has received");
}
}
运行结果:
6.2.3 TMessage Send<TMessage, TToken>(TMessage message, TToken token) where TMessage : class where TToken : IEquatable<TToken>
可通过注册的Token来区分通道,只会接收到发送的Token一致的消息
反编译:
TMessage Send<TMessage, TToken>(TMessage message, TToken token) where TMessage : class where TToken : IEquatable<TToken>;
使用举例:
IMessenger messenger = WeakReferenceMessenger.Default;
var l1 = new Listener(messenger, "A", m => $"l1 received:{m}".Dump());
var l2 = new Listener(messenger, "B", m => $"l2 received:{m}".Dump());
messenger.Send(new SimpleMessage("Hello"), "A");
messenger.Send(new SimpleMessage("World"), "B");
class Listener
{
public Listener(IMessenger messenger, string channel, Action<string> response)
{
messenger.Register<SimpleMessage, string>(this, channel, (_,m)=> response(m.Content));
}
}
record SimpleMessage(string Content);
运行结果
l1 received:Hello
l2 received:World
6.2.4 PropertyChangedMessage<T>
使用举例:
var rec = new ReceiveViewModel() { IsActive = true };
var sender = new SendViewModel();
sender.Data = "Hello";
class ReceiveViewModel : ObservableRecipient, IRecipient<PropertyChangedMessage<string>>
{
public void Receive(PropertyChangedMessage<string> message)
{
message.Dump($"{GetType().Name} has received");
}
}
partial class SendViewModel : ObservableObject
{
private string data;
public string Data
{
get => data;
set
{
if (SetProperty(ref data, value))
{
WeakReferenceMessenger.Default.Send(new PropertyChangedMessage<string>(this, nameof(Data), default, value));
}
}
}
}
运行结果:
6.3 接收消息
在本文中的代码中已出现多次,不再赘述。
6.4 特殊Attribute
6.4.1 NotifyPropertyChangedRecipients
如果在继承自 ObservableRecipient 的类型中声明属性,则可以使用 NotifyPropertyChangedRecipients 特性指示生成器还要插入代码,以针对属性更改发送一则说明属性已更改的消息。
使用举例:
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Messaging.Messages;
using CommunityToolkit.Mvvm.Messaging;
var rec1 = new ReceiveViewModel1() { IsActive = true };
var rec2 = new ReceiveViewModel2() { IsActive = true };
var sender = new SendViewModel();
sender.Data = "Hello";
class ReceiveViewModel1 : ObservableRecipient, IRecipient<PropertyChangedMessage<string>>
{
public void Receive(PropertyChangedMessage<string> message)
{
Console.WriteLine($"{GetType().Name} has received");
}
}
class ReceiveViewModel2 : ObservableRecipient, IRecipient<PropertyChangedMessage<string>>
{
public void Receive(PropertyChangedMessage<string> message)
{
Console.WriteLine($"{GetType().Name} has received");
}
}
partial class SendViewModel : ObservableRecipient
{
[ObservableProperty]
[NotifyPropertyChangedRecipients]
private string? data;
}
运行结果:
ReceiveViewModel1 has received
ReceiveViewModel2 has received