博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Messenger 深入
阅读量:5234 次
发布时间:2019-06-14

本文共 8660 字,大约阅读时间需要 28 分钟。

1、Messager交互结构和消息类型

衔接上篇,Messeger是信使的意思,顾名思义,他的目是用于View和ViewModel 以及 ViewModel和ViewModel 之间的消息通知和接收。

Messenger类用于应用程序的通信,接受者只能接受注册的消息类型,另外目标类型可以被指定,用Send<TMessage, TTarget>(TMessage message)实现,在这种情况下信息只能被传递如果接受者类型和目标参数类型匹配,

message可以是任何简单或者复杂的对象,你可以用特定的消息类型或者创建你自己的类型继承自他们。

 

交互结构如下所示:

 

 

消息类型如下表所示:

message消息对象类型 说明
MessageBase 简单的消息类,携带可选的信息关于消息发布者的
GenericMessage<T> 泛型消息
NotificationMessage 用于发送一个string类型通知给接受者
NotificationMessage<T>

和上面一样是一个,且具有泛型功能

NotificationMessage 向接受者发送一个通知,允许接受者向发送者回传消息
NotificationMessageAction<T> NotificationMessage的泛型方式
DialogMessage 发送者(通常是View)显示对话,并且传递调用者得回传结果(用于回调),接受者可以选择怎样显示对话框,可以使是标准的MessageBox也可也是自定义弹出窗口
PropertyChangedMessage<T> 用于广播一个属性的改变在发送者里,和PropertyChanged事件有完全箱体内各的目的,但是是一种弱联系方式

 

2、注册消息的模式

 上篇给出了注册的方法,但是注册可以有很多种方式,最常见的就是命名方法调用和Lambda表达式调用的方式:

2.1、基本的命名方法注册

1   // 使用命名方法进行注册 2   Messenger.Default.Register
(this, HandleMessage); 3 4 //卸载当前(this)对象注册的所有MVVMLight消息 5 this.Unloaded += (sender, e) => Messenger.Default.Unregister(this); 6 7 8 private void HandleMessage(String msg) 9 {10 //Todo11 }

2.2、使用 Lambda 注册

1   Messenger.Default.Register
(this, message => {2 // Todo 3 4 }); 5 //卸载当前(this)对象注册的所有MVVMLight消息6 this.Unloaded += (sender, e) => Messenger.Default.Unregister(this);

  

3、跨线程访问

之前在第8篇《》中,

我们有讨论过在异步线程中使用事件来执行和获取相关的执行步骤。但是如果异步线程中的某个方法需要操作主线程(UI线程的时候)的UI是不允许的。

Windows 中的控件被绑定到特定的UI线程(主线程)中,其他线程是不允许访问的,因为不具备线程安全性和规范性。所以后来MVVM Light才有了调度帮助类(DispatchHelper)来处理不用线程中的调度方案。

从这边可以衍生到异步线程下,对UI线程(主线程)的信息发送和接收。所以之前的代码 DispatchHelper 可以改装如下:

 注册模块(ViewModel中):

1  public class MessengerForDispatchViewModel:ViewModelBase 2     { 3         ///  4         /// 构造函数 5         ///  6         public MessengerForDispatchViewModel() 7         { 8             InitData(); 9             DispatcherHelper.Initialize();10 11             ///Messenger:信使12             ///Recipient:收件人          13             Messenger.Default.Register
(this, "UserMessenger", FeedBack);14 }15 }

 发送模块(异步线程中代码):

1   private void Start() 2         { 3             TopUserInfo ui = new TopUserInfo(); 4  5             //ToDo:编写创建用户的DataAccess代码 6             for (Int32 idx = 1; idx <= 9; idx++) 7             { 8                 Thread.Sleep(1000); 9                 ui = new TopUserInfo() {10                     isFinish = false,11                     process = idx*10,12                      userInfo =null13                 };14                 DispatcherHelper.CheckBeginInvokeOnUI(() =>15                 {16                     Messenger.Default.Send
(ui, "UserMessenger");17 });18 }19 Thread.Sleep(1000);20 ui = new TopUserInfo()21 {22 isFinish = true,23 process = 100,24 userInfo = up25 };26 DispatcherHelper.CheckBeginInvokeOnUI(() =>27 {28 Messenger.Default.Send
(ui, "UserMessenger");29 });30 }

 结果:

 

 

4、释放注册信息:

 4.1、基于View界面内的UnRegister的释放(为当前视图页面的Unload事件 附加 释放注册信息的功能):

  1 //卸载当前(this)对象注册的所有MVVMLight消息 2 this.Unloaded += (sender, e) => Messenger.Default.Unregister(this); 

 4.2、基于ViewModel类中的UnRegister释放(用户在关闭使用页面的时候同事调用该方法,释放注册,这个需要开发人员在关闭视图模型的时候发起): 

1  /// 2  /// 手动调用释放注册信息(该视图模型内的所有注册信息全部释放)3  /// 4  public void ReleaseRegister()5  {6    Messenger.Default.Unregister(this);             7  }

  

5、释放注册信息和内存处理

为了避免不必要的内存泄漏, .Net框架提出了比较实用的 WeakReference(弱引用)对象。该功能允许将对象的引用进行弱存储。如果对该对象的所有引用都被释放了,则垃圾回收机便可回收该对象。

类似将所有的注册信息保存在一个弱引用的存储区域,一旦注册信息所寄宿的宿主(View或者ViewModel)被释放,引用被清空,该注册信息也会在一定时间内被释放。

下面一个表格来源于  对 MVVM 的说明文档:

未取消注册时的内存泄漏风险:

可见性 WPF    Silverlight Windows Phone 8 Windows 运行时
静态 无风险 无风险 无风险 无风险
公共 无风险 无风险 无风险 无风险
内部 无风险 风险 风险 无风险
专用 无风险 风险 风险 无风险
匿名Lambda 无风险 风险 风险 无风险

 

 

6、专有信道和广播信道

 6.1 过滤Messenger发送端(通过判断发送端来确认是否是发送给自己的):

1  public class ForSourceSenderViewModel:ViewModelBase 2     { 3         public ForSourceSenderViewModel(){} 4  5         #region 全局命令 6         private RelayCommand sendMsg; 7         ///  8         /// 发送消息 9         /// 10         public RelayCommand SendMsg11         {12             get13             {14                 if (sendMsg == null) sendMsg = new RelayCommand(() => ExcuteSendMsh());15                 return sendMsg;16             }17 18             set19             {20                 sendMsg = value;21             }22         }23 24         #endregion25 26         #region 附属方法27         private void ExcuteSendMsh()28         {29             NotificationMessage nm = new NotificationMessage(this,"发送源消息");30             Messenger.Default.Send
(nm);31 }32 #endregion33 34 }

 

1             Messenger.Default.Register
(this, message =>2 {3 if (message.Sender is ForSourceSenderViewModel)4 {5 // 判断来源来接受消息6 MsgInfo = message.Notification;7 }8 });

  

6.2 开设专用的Messenger通道:

1   private Messenger myMessenger; 2         public MessengerForSourceViewModel() 3         { 4             //构造函数     5             myMessenger = new Messenger(); 6             SimpleIoc.Default.Register(() => myMessenger, "MyMessenger"); //注入一个Key为MyMessenger的Messenger对象 7  8             myMessenger.Register
(this, message => //注册myMessenger,开启监听 9 {10 // 判断来源来接受消息11 MsgInfo = message.Notification;12 });13 } 
1    #region 全局命令 2         private RelayCommand sendMsg; 3         ///  4         /// 发送消息 5         ///  6         public RelayCommand SendMsg 7         { 8             get 9             {10                 if (sendMsg == null) sendMsg = new RelayCommand(() => ExcuteSendMsh());11                 return sendMsg;12             }13             set14             {15                 sendMsg = value;16             }17         }18                 #endregion19 20         #region 附属方法21         private void ExcuteSendMsh()22         {23             NotificationMessage nm = new NotificationMessage(this,String.Format("发送消息:{0}",DateTime.Now));24             Messenger myMessenger = SimpleIoc.Default.GetInstance
("MyMessenger");//获取已存在的Messenger实例25 myMessenger.Send
(nm);//消息发送26 }27 #endregion

 

 

6.3 使用令牌(Token)区分和使用信道:这是最常用的方式。使用专属Token,可以区分不同的信道,并提高复用性。

Messenger中包含一个token参数,发送方和注册方使用同一个token,便可保证数据在该专有信道中流通,所以令牌是筛选和隔离消息的最好办法。

1  //以ViewAlert位Tokon标志,进行消息发送2  Messenger.Default.Send
("ViewModel通知View弹出消息框", "ViewAlert");

 

1         public MessagerForView() 2         { 3             InitializeComponent(); 4  5             //消息标志token:ViewAlert,用于标识只阅读某个或者某些Sender发送的消息,并执行相应的处理,所以Sender那边的token要保持一致 6             //执行方法Action:ShowReceiveInfo,用来执行接收到消息后的后续工作,注意这边是支持泛型能力的,所以传递参数很方便。 7             Messenger.Default.Register
(this, "ViewAlert", ShowReceiveInfo); 8 this.DataContext = new MessengerRegisterForVViewModel(); 9 //卸载当前(this)对象注册的所有MVVMLight消息10 this.Unloaded += (sender, e) => Messenger.Default.Unregister(this);11 }12 13 ///
14 /// 接收到消息后的后续工作:根据返回来的信息弹出消息框15 /// 16 ///
17 private void ShowReceiveInfo(String msg)18 {19 MessageBox.Show(msg);20 }

 

 7、使用内置消息

比如我们上面用到的 NotificationMessage<T> ,以及PropertyChanged­Message<T>。

 Notification是一种消息通知机制;而PropertyChanged­Message主要指的是当属性改变的时候,执行通知操作。

1   public class PropertyChangedViewModel:ViewModelBase 2     { 3         public const string PropertyName = "UserName"; //注册为该属性,该属性变化时进行消息发送 4         public PropertyChangedViewModel() { } 5  6         #region 全局变量 7         private String userName; 8         ///  9         /// 用户名称10         /// 11         public string UserName12         {13             get14             {15                 return userName;16             }17 18             set19             {20                 String oldValue = userName;21                 userName = value;22                 RaisePropertyChanged(()=>UserName,oldValue,value,true);//这边相应配置上发送参数23             }24         }25         #endregion
1             Messenger.Default.Register
>(this, message =>2 {3 if (message.PropertyName == PropertyChangedViewModel.PropertyName) //接受特定属性值相关信道的消息4 {5 PropertyChangedInfo = (message.OldValue + " --> " + message.NewValue);//输出旧值到新值的内容6 }7 });

 

结果:

 

转载于:https://www.cnblogs.com/happyyftk/p/6903775.html

你可能感兴趣的文章
Web 站点的水平扩展和垂直扩展 (译文)
查看>>
把DataTable中的身份证号导出excel的解决方案
查看>>
大三中期的鸭梨
查看>>
LeetCode-Palindrome Number
查看>>
C++ 哈希表
查看>>
一个 Java 的 Socket 服务器和客户端通信的例子
查看>>
poj 1113 Wall 凸包
查看>>
菜鸟开发WP APP…
查看>>
IntelliJ IDEA 中创建maven项目
查看>>
while循环的初始以及编码的初始
查看>>
关于数据库的建立及增删改查
查看>>
vs2010 ASP.NET, C#, Ajax 页面局部更新
查看>>
xmlSpy套件(Altova MissionKit 2016)的Ollydbg调试过程及破解
查看>>
无人驾驶技术之Kalman Filter原理介绍
查看>>
【BZOJ2002】[HNOI2010] 弹飞绵羊(大力分块)
查看>>
初学MillerRabin素数测试
查看>>
zoj1276 Optimal Array Multiplication Sequence(DP)
查看>>
BST_insert
查看>>
upper_bound下确界
查看>>
407. Trapping Rain Water II
查看>>