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<String>(this, HandleMessage);
  3.  
  4. //卸载当前(this)对象注册的所有MVVMLight消息
  5. this.Unloaded += (sender, e) => Messenger.Default.Unregister(this);
  6.  
  7. private void HandleMessage(String msg)
  8. {
  9. //Todo
  10. }

2.2、使用 Lambda 注册

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

3、跨线程访问

之前在第8篇《利刃 MVVMLight 8:DispatchHelper在多线程和调度中的使用》中,

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

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

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

注册模块(ViewModel中):

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

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

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

结果:

4、释放注册信息:

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

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

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

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

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

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

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

下面一个表格来源于 Laurent Bugnion 对 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. /// <summary>
  8. /// 发送消息
  9. /// </summary>
  10. public RelayCommand SendMsg
  11. {
  12. get
  13. {
  14. if (sendMsg == null) sendMsg = new RelayCommand(() => ExcuteSendMsh());
  15. return sendMsg;
  16. }
  17.  
  18. set
  19. {
  20. sendMsg = value;
  21. }
  22. }
  23.  
  24. #endregion
  25.  
  26. #region 附属方法
  27. private void ExcuteSendMsh()
  28. {
  29. NotificationMessage nm = new NotificationMessage(this,"发送源消息");
  30. Messenger.Default.Send<NotificationMessage>(nm);
  31. }
  32. #endregion
  33.  
  34. }
  1. Messenger.Default.Register<NotificationMessage>(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<NotificationMessage>(this, message => //注册myMessenger,开启监听
  9. {
  10. // 判断来源来接受消息
  11. MsgInfo = message.Notification;
  12. });
  13. } 
  1. #region 全局命令
  2. private RelayCommand sendMsg;
  3. /// <summary>
  4. /// 发送消息
  5. /// </summary>
  6. public RelayCommand SendMsg
  7. {
  8. get
  9. {
  10. if (sendMsg == null) sendMsg = new RelayCommand(() => ExcuteSendMsh());
  11. return sendMsg;
  12. }
  13. set
  14. {
  15. sendMsg = value;
  16. }
  17. }
  18. #endregion
  19.  
  20. #region 附属方法
  21. private void ExcuteSendMsh()
  22. {
  23. NotificationMessage nm = new NotificationMessage(this,String.Format("发送消息:{0}",DateTime.Now));
  24. Messenger myMessenger = SimpleIoc.Default.GetInstance<Messenger>("MyMessenger");//获取已存在的Messenger实例
  25. myMessenger.Send<NotificationMessage>(nm);//消息发送
  26. }
  27. #endregion

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

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

  1. //以ViewAlert位Tokon标志,进行消息发送
  2. Messenger.Default.Send<String>("ViewModel通知View弹出消息框", "ViewAlert");
  1. public MessagerForView()
  2. {
  3. InitializeComponent();
  4.  
  5. //消息标志token:ViewAlert,用于标识只阅读某个或者某些Sender发送的消息,并执行相应的处理,所以Sender那边的token要保持一致
  6. //执行方法Action:ShowReceiveInfo,用来执行接收到消息后的后续工作,注意这边是支持泛型能力的,所以传递参数很方便。
  7. Messenger.Default.Register<String>(this, "ViewAlert", ShowReceiveInfo);
  8. this.DataContext = new MessengerRegisterForVViewModel();
  9. //卸载当前(this)对象注册的所有MVVMLight消息
  10. this.Unloaded += (sender, e) => Messenger.Default.Unregister(this);
  11. }
  12.  
  13. /// <summary>
  14. /// 接收到消息后的后续工作:根据返回来的信息弹出消息框
  15. /// </summary>
  16. /// <param name="msg"></param>
  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. /// <summary>
  9. /// 用户名称
  10. /// </summary>
  11. public string UserName
  12. {
  13. get
  14. {
  15. return userName;
  16. }
  17.  
  18. set
  19. {
  20. String oldValue = userName;
  21. userName = value;
  22. RaisePropertyChanged(()=>UserName,oldValue,value,true);//这边相应配置上发送参数
  23. }
  24. }
  25. #endregion
  1. Messenger.Default.Register<PropertyChangedMessage<String>>(this, message =>
  2. {
  3. if (message.PropertyName == PropertyChangedViewModel.PropertyName) //接受特定属性值相关信道的消息
  4. {
  5. PropertyChangedInfo = (message.OldValue + " --> " + message.NewValue);//输出旧值到新值的内容
  6. }
  7. });

结果:

示例代码下载

转载请注明出处,谢谢

利刃 MVVMLight 10:Messenger 深入的更多相关文章

  1. 利刃 MVVMLight

    已经很久没有写系列文章了,上一次是2012年写的HTLM5系列,想想我们应该是较早一批使用HTML5做项目的人. 相比我当时动不动100+的粉丝增长和两天3000+的阅读量,MVVM Light只能算 ...

  2. 利刃 MVVMLight 9:Messenger

    MVVM的目标之一就是为了解耦View和ViewModel.View负责视图展示,ViewModel负责业务逻辑处理,尽量保证 View.xaml.cs中的简洁,不包含复杂的业务逻辑代码. 但是在实际 ...

  3. 利刃 MVVMLight 3:双向数据绑定

          上篇我们已经了解了MVVM的框架结构和运行原理.这里我们来看一下伟大的双向数据绑定. 说到双向绑定,大家比较熟悉的应该就是AngularJS了,几乎所有的AngularJS 系列教程的开篇 ...

  4. 利刃 MVVMLight 5:绑定在表单验证上的应用

    表单验证是MVVM体系中的重要一块.而绑定除了推动 Model-View-ViewModel (MVVM) 模式松散耦合 逻辑.数据 和 UI定义 的关系之外,还为业务数据验证方案提供强大而灵活的支持 ...

  5. 利刃 MVVMLight 6:命令基础

    在MVVM Light框架中,事件是WPF应用程序中UI与后台代码进行交互的最主要方式,与传统方式不同,mvvm中主要通过绑定到命令来进行事件的处理, 因此要了解mvvm中处理事件的方式,就必须先熟悉 ...

  6. 利刃 MVVMLight 7:命令深入

    上面一篇我们大致了解了命令的基本使用方法和基础原理,但是实际在运用命令的时候会复杂的多,并且有各种各样的情况. 一.命令带参数的情况: 如果视图控件所绑定的命令想要传输参数,需要配置 CommandP ...

  7. 利刃 MVVMLight 1:MVVMLight介绍以及在项目中的使用

    一.MVVM 和 MVVMLight介绍 MVVM是Model-View-ViewModel的简写.类似于目前比较流行的MVC.MVP设计模式,主要目的是为了分离视图(View)和模型(Model)的 ...

  8. 利刃 MVVMLight 2:Model、View、ViewModel结构以及全局视图模型注入器的说明

         上一篇我们已经介绍了如何使用NuGet把MVVMLight应用到我们的WPF项目中.这篇我们来了解下一个基本的MVVMLight框架所必须的结构和运行模式. MVVMLight安装之后,我们 ...

  9. 利刃 MVVMLight 4:绑定和绑定的各种使用场景

    一.绑定: 主要包含元素绑定和非元素绑定两种. 1.元素绑定,是绑定的最简单形式,源对象是WPF的元素,并且源对象的属性是依赖项属性. 根据我们之前的知识 ,依赖项属性具有内置的更改通知支持.所以当我 ...

随机推荐

  1. 浅谈css中单位px和em,rem的区别-转载

    px是你屏幕设备物理上能显示出的最小的一个点,这个点不是固定宽度的,不同设备上点的长宽.比例有可能会不同.假设:你现在用的显示器上1px宽=1毫米,但我用的显示器1px宽=两毫米,那么你定义一个div ...

  2. javascript中的几种遍历方法浅析

    1. for...in 用于对数组或者对象的属性的可枚举属性进行循环操作.注意该对象来自原型链上的可枚举属性也会被循环.下面看例子 var arr = ["lee","h ...

  3. struts2之拦截器

    1. 为什么需要拦截器 早期MVC框架将一些通用操作写死在核心控制器中,致使框架灵活性不足.可扩展性降低, Struts 2将核心功能放到多个拦截器中实现,拦截器可自由选择和组合,增强了灵活性,有利于 ...

  4. jsp之session对象

    jsp之session对象:一:概念session对象可以在应用程序的web页面之间跳转时保存用户的信息,使整个用户会话一直存在,直到关闭浏览器或是销毁session.session的生命周期:20~ ...

  5. CSS样式表初学,比C#和JS简单

    今天咱们一起来看下CSS样式表的基本基础 经常看博客或者喜欢钻研代码这一类的人对CSS可能有所耳闻,但具体的可能不是很清楚 那什么是CSS呢?与HTML又有什么区别呢?今天咱们就来说道下这个CSS C ...

  6. java OJ题目判断输入结束(与C语言的EOF结束等价)

    /* * java 作Oj题目是会有输入若干数据的情况,不好判断输入结束符, * 类似于C语言中的EOF符号 * 在这里提供了一种方法 * */ import java.util.Iterator; ...

  7. xmlplus 组件设计系列之二 - 按钮

    除了图标以外,按钮也许是最简单的组件了,现在来看看如何定义按钮组件. 使用原生按钮组件 在 xmlplus 中,HTML 元素也以组件的方式存在.所以,你可以直接通过使用 button 标签或者 in ...

  8. WEB认证模式:Basic & Digest

    一. HTTP Basic 客户端以" : "连接用户名和密码后,再经BASE64加密通过Authorization请求头发送该密文至服务端进行验证,每次请求都需要重复发送该密文. ...

  9. 03(3) 基于GMM-HMM的SR基础

    1.GMM-HMM的训练 1)训练GSM-HMM (1)确定HMM拓扑结构 (2)初始化HMM模型参数 (3)在所有的utterances中计算所需的统计量 (4)使用公式更新模型参数 (5)不收敛, ...

  10. Python 操作 MySQL 的正确姿势

    欢迎大家关注腾讯云技术社区-博客园官方主页,我们将持续在博客园为大家推荐技术精品文章哦~ 作者:邵建永 使用Python进行MySQL的库主要有三个,Python-MySQL(更熟悉的名字可能是MyS ...