Messenger
MVVM的目标之一就是为了解耦View和ViewModel。View负责视图展示,ViewModel负责业务逻辑处理,尽量保证 View.xaml.cs中的简洁,不包含复杂的业务逻辑代码。
但是在实际情况中是View和ViewModel之间的交互方式还是比较复杂的,View和ViewModel的分离并不是界定的那么清晰。
比如以下两种场景:
1、如果需要某张视图页面弹出对话框、弹出子窗体、处理界面元素,播放动画等。如果这些操作都放在ViewModel中,就会导致ViewModel还是要去处理View级别的元素,造成View和ViewModel的依赖。
最好的办法就是ViewModel通知View应该做什么,而View监听接收到命令,并去处理这些界面需要处理的事情。
2、ViewModel和ViewModel之间也需要通过消息传递来完成一些交互。
而MVVM Light 的 Messenger类,提供了解决了上述两个问题的能力:
Messenger类用于应用程序的通信,接受者只能接受注册的消息类型,另外目标类型可以被指定,用Send<TMessage, TTarget>(TMessage message) 实现,
在这种情况下信息只能被传递如果接受者类型和目标参数类型匹配,message可以是任何简单或者复杂的对象,你可以用特定的消息类型或者创建你自己的类型继承自他们。
Messager类的主要交互模式就是信息接受和发送(可以理解为“发布消息服务”和“订阅消息服务”),是不是想到观察者模式了,哈哈哈。
MVVM Light Messenger 旨在通过简单的设计模式来精简此场景:任何对象都可以是接收端;任何对象都可以是发送端;任何对象都可以是消息。
如图:
1、View和ViewModel之间的消息交互
在View和ViewModel中进行消息器注册,相当于订阅服务。包含消息标志、消息参数和消息执行方法。如下:
消息标志token:ViewAlert,用于标识只阅读某个或者某些Sender发送的消息,并执行相应的处理,所以Sender那边的token要保持一致
执行方法Action:ShowReceiveInfo,用来执行接收到消息后的后续工作,注意这边是支持泛型能力的,所以传递参数很方便。
View.xaml.cs 代码如下:
1 public partial class NessagerForView : Window
2 {
3 public NessagerForView()
4 {
5 InitializeComponent();
6
7 //消息标志token:ViewAlert,用于标识只阅读某个或者某些Sender发送的消息,并执行相应的处理,所以Sender那边的token要保持一致
8 //执行方法Action:ShowReceiveInfo,用来执行接收到消息后的后续工作,注意这边是支持泛型能力的,所以传递参数很方便。
9 Messenger.Default.Register<String>(this, "ViewAlert", ShowReceiveInfo);
10 this.DataContext = new MessengerRegisterForVViewModel();
11 //卸载当前(this)对象注册的所有MVVMLight消息
12 this.Unloaded += (sender, e) => Messenger.Default.Unregister(this);
13 }
14
15 /// <summary>
16 /// 接收到消息后的后续工作:根据返回来的信息弹出消息框
17 /// </summary>
18 /// <param name="msg"></param>
19 private void ShowReceiveInfo(String msg)
20 {
21 MessageBox.Show(msg);
22 }
23 }
ViewModel代码:
1 public class MessengerRegisterForVViewModel:ViewModelBase
2 {
3
4 public MessengerRegisterForVViewModel()
5 {
6
7 }
8
9 #region 命令
10
11 private RelayCommand sendCommand;
12 /// <summary>
13 /// 发送命令
14 /// </summary>
15 public RelayCommand SendCommand
16 {
17 get
18 {
19 if (sendCommand == null)
20 sendCommand = new RelayCommand(() => ExcuteSendCommand());
21 return sendCommand;
22
23 }
24 set { sendCommand = value; }
25 }
26
27 private void ExcuteSendCommand()
28 {
29 Messenger.Default.Send<String>("ViewModel通知View弹出消息框", "ViewAlert"); //注意:token参数一致
30 }
31
32 #endregion
33 }
结果:
2、ViewModel和ViewModel之间的消息交互,ViewModel和ViewModel在很多种场景下也需要通过消息传递来完成一些交互。
比如我打开了两个视图,一个视图是用户信息列表,一个视图是用户信息添加页面,如果想要达到添加信息之后,用户信息列表视图实时刷新,用消息通知无疑是一个很棒的体验。
我们来模拟一下:
MessengerRegisterViewModel代码:
1 public class MessengerRegisterViewModel:ViewModelBase
2 {
3 public MessengerRegisterViewModel()
4 {
5 ///Messenger:信使
6 ///Recipient:收件人
7 Messenger.Default.Register<String>(this,"Message",ShowReceiveInfo);
8 }
9
10
11 #region 属性
12
13 private String receiveInfo;
14 /// <summary>
15 /// 接收到信使传递过来的值
16 /// </summary>
17 public String ReceiveInfo
18 {
19 get { return receiveInfo; }
20 set { receiveInfo = value; RaisePropertyChanged(()=>ReceiveInfo); }
21 }
22
23 #endregion
24
25
26 #region 启动新窗口
27
28 private RelayCommand showSenderWindow;
29
30 public RelayCommand ShowSenderWindow
31 {
32 get {
33 if (showSenderWindow == null)
34 showSenderWindow = new RelayCommand(()=>ExcuteShowSenderWindow());
35 return showSenderWindow;
36
37 }
38 set { showSenderWindow = value; }
39 }
40
41 private void ExcuteShowSenderWindow()
42 {
43 MessengerSenderView sender = new MessengerSenderView();
44 sender.Show();
45 }
46
47 #endregion
48
49
50 #region 辅助函数
51 /// <summary>
52 /// 显示收件的信息
53 /// </summary>
54 /// <param name="msg"></param>
55 private void ShowReceiveInfo(String msg)
56 {
57 ReceiveInfo += msg+"\n";
58 }
59 #endregion
60 }
MessengerSenderViewModel代码:
1 public class MessengerSenderViewModel:ViewModelBase
2 {
3 public MessengerSenderViewModel()
4 {
5
6 }
7
8 #region 属性
9 private String sendInfo;
10 /// <summary>
11 /// 发送消息
12 /// </summary>
13 public String SendInfo
14 {
15 get { return sendInfo; }
16 set { sendInfo = value; RaisePropertyChanged(()=>SendInfo); }
17 }
18
19 #endregion
20
21 #region 命令
22
23 private RelayCommand sendCommand;
24 /// <summary>
25 /// 发送命令
26 /// </summary>
27 public RelayCommand SendCommand
28 {
29 get
30 {
31 if (sendCommand == null)
32 sendCommand = new RelayCommand(() => ExcuteSendCommand());
33 return sendCommand;
34
35 }
36 set { sendCommand = value; }
37 }
38
39 private void ExcuteSendCommand()
40 {
41 Messenger.Default.Send<String>(SendInfo, "Message");
42 }
43
44 #endregion
45 }
结果如下: