Messeger是信使的意思,顾名思义,他的目是用于View和ViewModel 以及 ViewModel和ViewModel 之间的消息通知和接收。Messenger类用于应用程序的通信,接受者只能接受注册的消息类型,另外目标类型可以被指定,用Send<TMessage, TTarget>(TMessage message)实现,在这种情况下信息只能被传递如果接受者类型和目标参数类型匹配,message可以是任何简单或者复杂的对象,你可以用特定的消息类型或者创建你自己的类型继承自他们。
1、View和ViewModel之间的消息交互
在View和ViewModel中进行消息器注册,相当于订阅服务。包含消息标志、消息参数和消息执行方法。如下:
消息标志token:ViewAlert,用于标识只阅读某个或者某些Sender发送的消息,并执行相应的处理,所以Sender那边的token要保持一致
执行方法Action:ShowReceiveInfo,用来执行接收到消息后的后续工作,注意这边是支持泛型能力的,所以传递参数很方便。
public partial class NessagerForView : Window
{
public NessagerForView()
{
InitializeComponent();
//消息标志token:ViewAlert,用于标识只阅读某个或者某些Sender发送的消息,并执行相应的处理,所以Sender那边的token要保持一致
//执行方法Action:ShowReceiveInfo,用来执行接收到消息后的后续工作,注意这边是支持泛型能力的,所以传递参数很方便。
Messenger.Default.Register<String>(this, "ViewAlert", ShowReceiveInfo);
this.DataContext = new MessengerRegisterForVViewModel();
//卸载当前(this)对象注册的所有MVVMLight消息,
//基于View界面内的UnRegister的释放(为当前视图页面的Unload事件)可以指定令牌的名称,如“ViewAlert”
this.Unloaded += (sender, e) => Messenger.Default.Unregister(this);
}
/// <summary>
/// 接收到消息后的后续工作:根据返回来的信息弹出消息框
/// </summary>
/// <param name="msg"></param>
private void ShowReceiveInfo(String msg)
{
MessageBox.Show(msg);
}
//基于ViewModel类中的UnRegister释放(用户在关闭使用页面的时候同事调用该方法)
/// <summary>
/// 手动调用释放注册信息(该视图模型内的所有注册信息全部释放)
/// </summary>
public void ReleaseRegister()
{
Messenger.Default.Unregister(this);
//可以指定令牌的名称,如“ViewAlert”
//Messenger.Default.Unregister<object>(this, "ViewAlert");
}
}
ViewModel代码:
public class MessengerRegisterForVViewModel:ViewModelBase
{
public MessengerRegisterForVViewModel()
{
}
#region 命令
private RelayCommand sendCommand;
/// <summary>
/// 发送命令
/// </summary>
public RelayCommand SendCommand
{
get
{
if (sendCommand == null)
sendCommand = new RelayCommand(() => ExcuteSendCommand());
return sendCommand;
}
set { sendCommand = value; }
}
private void ExcuteSendCommand()
{
Messenger.Default.Send<String>("ViewModel通知View弹出消息框", "ViewAlert");
//注意:token参数一致
}
#endregion
}
参考地址:https://www.cnblogs.com/wzh2010/p/6679025.html
2、ViewModel和ViewModel之间的消息交互
消息接收者:MessengerRegisterViewModel代码
public class MessengerRegisterViewModel:ViewModelBase
{
public MessengerRegisterViewModel()
{
//信息接收者,注册信息接收的事件。
//令牌是Message,传递的是String,收到后执行ShowReceiveInfo事件
Messenger.Default.Register<String>(this,"Message",ShowReceiveInfo);
}
private void ShowReceiveInfo(String msg)
{
ReceiveInfo += msg+"\n";
}
#region 属性
private String receiveInfo;
/// <summary>
/// 接收并保存信使传递过来的值
/// </summary>
public String ReceiveInfo
{
get { return receiveInfo; }
set { receiveInfo = value; RaisePropertyChanged(()=>ReceiveInfo); }
}
#endregion
#region 命令方式启动新窗口,消息从新窗口传回来。
private RelayCommand showSenderWindow;
public RelayCommand ShowSenderWindow
{
get {
if (showSenderWindow == null)
showSenderWindow = new RelayCommand(()=>ExcuteShowSenderWindow());
return showSenderWindow;
}
set { showSenderWindow = value; }
}
private void ExcuteShowSenderWindow()
{
MessengerSenderView sender = new MessengerSenderView();
sender.Show();
}
#endregion
}
信息发送者: MessengerSenderViewModel代码
public class MessengerSenderViewModel:ViewModelBase
{
public MessengerSenderViewModel()
{
}
#region 属性
private String sendInfo;
/// <summary>
/// 要发送的消息
/// </summary>
public String SendInfo
{
get { return sendInfo; }
set { sendInfo = value; RaisePropertyChanged(()=>SendInfo); }
}
#endregion
#region 通过命令,调用方法,广播发送消息
private RelayCommand sendCommand;
/// <summary>
/// 发送命令
/// </summary>
public RelayCommand SendCommand
{
get
{
if (sendCommand == null)
sendCommand = new RelayCommand(() => ExcuteSendCommand());
return sendCommand;
}
set { sendCommand = value; }
}
private void ExcuteSendCommand()
{
//用Message令牌,发送SendInfo字段
Messenger.Default.Send<String>(SendInfo, "Message");
}
#endregion
}
3、MVVMLight中消息通知机制的对外实现
/// <summary>
/// 注册订阅事件
/// </summary>
/// <typeparam name="TMessage">传参类型</typeparam>
/// <param name="recipient">订阅实例</param>
/// <param name="token">发送与接收定义的key值</param>
/// <param name="action">订阅事件触发的action</param>
/// <param name="keepTargetAlive">持续控制实例存在,防止被回收清除</param>
public virtual void Register<TMessage>(object recipient,object token,Action<TMessage> action,bool keepTargetAlive = false)
/// <summary>
/// 被订阅方触发事件
/// </summary>
/// <typeparam name="TMessage">传参类型</typeparam>
/// <param name="message">参数数据</param>
/// <param name="token">key</param>
public virtual void Send<TMessage>(TMessage message, object token)
- 该工具的内部主要逻辑是以字典模式进行储存持有订阅对象设置的传入参数Type类型、Key值、Action、Target(订阅对象本身)
- 在发生订阅事件和触发订阅事件时都会调用通过查询上述字典进行匹配,找到Key值相同&&Type类型相同的值,执行target的action并传入参数
- 扩展1:可发生没有key值的订阅事件,只要传参类型相同就可以
- 扩展2:可发生静态类的订阅事件,其内部有判断是否为static,static时target可为空
简单 例子:
public class Class1
{
public Class1()
{
Messenger.Default.Register<string>(this, "key", DataChangedHandle);
}
//订阅触发事件
public void DataChangedHandle(string obj)
{
//dosomething
}
/// <summary>
///对外通知事件消息
/// </summary>
public void SendMessage()
{
Messenger.Default.Send("Message", "key");
}
}
- 订阅时传参的“Key”是发送与接收之间唯一的key值,它的类型是object类型,也就是说可以根据自身需求进行设置key值类型,不仅是用字符,还可以用字段,或者类点字段的形式。
- Messenger其内部逻辑是在对Key值和传参类型进行比对,完全符合时才会调用订阅方实例的action
- Messenger有多重重载,订阅时不设置key,对任何传参类型相同的调用都接收触发
-
有注册肯定有注销,如果不注销的话,这个注册会一直存在。所以在关闭窗体的时候或者需要停止接收消息的时候来注销
消息类型如下表所示:
message消息对象类型 | 说明 |
MessageBase | 简单的消息类,携带可选的信息关于消息发布者的 |
GenericMessage<T> | 泛型消息 |
NotificationMessage | 用于发送一个string类型通知给接受者 |
NotificationMessage<T> | 和上面一样是一个,且具有泛型功能 |
NotificationMessage | 向接受者发送一个通知,允许接受者向发送者回传消息 |
NotificationMessageAction<T> | NotificationMessage的泛型方式 |
DialogMessage | 发送者(通常是View)显示对话,并且传递调用者得回传结果(用于回调),接受者可以选择怎样显示对话框,可以使是标准的MessageBox也可也是自定义弹出窗口 |
PropertyChangedMessage<T> | 用于广播一个属性的改变在发送者里,和PropertyChanged事件有完全箱体内各的目的,但是是一种弱联系方式 |
4、注册消息的模式
上篇给出了注册的方法,但是注册可以有很多种方式,最常见的就是命名方法调用和Lambda表达式调用的方式:
4.1、基本的命名方法注册
// 使用命名方法进行注册,没有指定令牌,接收全部信息。然后调用HandleMessage方法。
Messenger.Default.Register<String>(this, HandleMessage);
//卸载当前(this)对象注册的所有MVVMLight消息
this.Unloaded += (sender, e) => Messenger.Default.Unregister(this);
private void HandleMessage(String msg)
{
//Todo
}
4.2、使用 Lambda 注册
//没有指定令牌,接收全部信息。
Messenger.Default.Register<String>(this, message =>
{
// Todo 这里写执行的方法
});
//窗口关闭时,卸载当前(this)对象注册的所有MVVMLight消息
this.Unloaded += (sender, e) => Messenger.Default.Unregister(this);
5、DialogMessage和 NotificationMessageAction
DialogMessage发送代码:
//通过命令发送令牌信息
public ICommand TestMathCommand
{
get
{
return new RelayCommand(() =>
{
DialogMessage msg = new DialogMessage("提示正文", MessageBoxCallBack);
//设置弹出的MessageBox的类型,包含两个按钮,一个"确认", 一个"取消"
msg.Button = MessageBoxButton.OKCancel;
msg.Caption = "标题";
Messenger.Default.Send<DialogMessage>(msg);
});
}
}
private void MessageBoxCallBack(MessageBoxResult ret)
{
if (ret == MessageBoxResult.OK)
{
MessageBox.Show("弹窗点击了确认。继续执行。");
}
}
DialogMessage接收代码:
public MainWindow()
{
InitializeComponent();
Messenger.Default.Register<DialogMessage>(this, dlgmsg =>
{
var res = MessageBox.Show(dlgmsg.Content, dlgmsg.Caption, dlgmsg.Button);
dlgmsg.Callback(res); //这句会此起ViewModel中的MessageBoxCallBack调用
});
}
DialogMessage包含了弹出框需要的全部信息,包括标题,正文和回调函数。在这个Demo中需要注意一下MessageBox是模态的,因此当执行到MessageBox.Show()时,执行流会卡住,等到用户点击"OK"惑"Cancel"按钮后,程序才会继续执行。
但是最新版的 MVVMLight会提示,这种方法已经过时……
NotificationMessageAction发送代码:
//通过命令发送令牌信息
public ICommand TestMathCommand
{
get
{
return new RelayCommand(() =>
{
NotificationMessageAction<MessageBoxResult> msg = new NotificationMessageAction<MessageBoxResult>("主页面测试", (ret) =>
{
if (ret == MessageBoxResult.OK)
{
MessageBox.Show("弹窗点击了确认。继续执行。");
}
}
);
Messenger.Default.Send<NotificationMessageAction<MessageBoxResult>>(msg);
});
}
}
NotificationMessageAction接收代码:
public MainWindow()
{
InitializeComponent();
Messenger.Default.Register<NotificationMessageAction<MessageBoxResult>>(this, (msg) =>
{
var ret = MessageBox.Show("内容", "标题", MessageBoxButton.OKCancel);
//msg.Notification内容是“主页面测试”
if (msg.Notification != null)
{
msg.Execute(ret);
}
}
);
}
如果ViewModel需要向View发送一个对象,可以使用 NotificationMessage<类型>的泛型版本。