`
acw97acw
  • 浏览: 20787 次
最近访客 更多访客>>
社区版块
存档分类
最新评论

MVVMLight源码分析之消息机制和ViewModelBase

 
阅读更多

MVVMLight源码分析之消息机制和ViewModelBase
2011年01月30日
  2010-11-06 18:24 by 撞破南墙, 789 visits, 网摘, 收藏, 编辑  目录 1 介绍 MVVMLight 2 代码分析方法
  3 具体剖析
  3.1 消息机制的剖析 
  3.2 ViewModelBase
  3.3 Command 介绍/EventToCommand behavior       
  3.4 DispatcherHelper  试用范围: Windows Presentation Foundation, Silverlight and for Windows Phone 7
  作者的介绍  MessageBase: A simple message class, carrying optional information about the message's sender.
  GenericMessage: A simple message with a Content property of type T.
  NotificationMessage: Used to send a notification (as a string) to a recipient. For example, save your notifications as constant in a Notifications class, and then send Notifications.Save to a recipient.
  NotificationMessage: Same as above, but with a generic Content property. Can be used to pass a parameter to the recipient together with the notification.
  NotificationMessageAction: Sends a notification to a recipient and allows the recipient to call the sender back.
  NotificationMessageAction: Sends a notification to a recipient and allows the recipient to call the sender back with a generic parameter.
  DialogMessage: Used to request that a recipient (typically a View) displays a dialog, and passes the result back to the caller (using a callback). The recipient can choose how to display the dialog, either with a standard MessageBox, with a custom popup, etc…
  PropertyChangedMessage: Used to broadcast that a property changed in the sender. Fulfills the same purpose than the PropertyChanged event, but in a less tight way.
  Command classes optimized for WPF and Silverlight and simplifying commanding in your application. Available with or without generic parameter (RelayCommand and RelayCommand). For an in-depth analysis, I encourage you to read Using RelayCommands in Silverlight and WPF
  The GalaSoft.MvvmLight.Extras library with optional classes: 
  EventToCommand behavior, allowing you to bind any event of any UI element to an ICommand, for example on the ViewModel, directly in XAML. This makes using Commands much easier, without writing code behind. With the newest version, you can even get the EventArgs of the fired event directly in the ViewModel to handle it.
  DispatcherHelper class, a lightweight class helping you to create multithreaded applications.
  2.1 按照骑士的建议,先从测试代码看起。完整了解其功能。
  2.2 使用类图看其结构。
  3.3 顾名思义+debug看其执行。 
  之前我还去TOPLANGUAGE整理了一下牛牛们的想法,也一并贴出来供大家参考。排版乱抱歉。   1 画UML. 由表及里,由浅入深,由轮廓到详细。 画完了也就分析的八九不离十了。 这个过程需要经验的积累。。刚开始会比较难,后来会越顺手。
  2我感觉自己看的效率还是比较高,也是喜欢看底层,弄明白每个类干什么的,除非源码不可见。刚开始看时不去理会每个类的具体实现,而 
  毕竟
  代码是是从入口(比如一个库暴露的比较重要的API)一层一层往里看,先理清那些主要的类之间的调用关系,在脑力里大致形成了一张类关系图之后再看实现细节。在源码不可见的情况下,通过它们暴露的接口组合也基本能猜出里边的一些实现机制(是猜测,不一定正确了).
  3
  先考虑一个模块 
  a,打开class view,根据class name 来判断这个类的功能。在心里对每一个类功能,已经类之间的相互关系有一个底。 
  想想如果你是这模块的设计者,你应该怎么去设计?一般来说,有经验的程序员代码都会按照MVC模式去设计代码, 
  并且每一个类的功能都会非常简洁。 
  b,读class,先看私有变量,一个class中哪些数据,这是一个class的核心,大部分操作都是相对数据来操作的。 
  然后看接口。
  模块之间耦合 
  如果你是设计者你应该怎么去设计?比如要你来设计3D游戏引擎?站在设计者的角度,用设计者的思维去多考虑问题。
  4 有本书叫做code reading。
  5 divide and conquer
  6
  我倒是读过十来万行的服务端软件的代码,不过说是十多万行,其实我看的也就很小一部分。成熟、设计良好的软件,其主干脉络是很清晰的。 
  从入口开始,看其初始化过程,可以基本上把握其内部的各类数据结构以及其之间的关系。 
  初始化过程之后,如果是面向过程的代码,就找其主体逻辑;如果是事件驱动的,就找其事件分发部分的代码。
  最怕的是看面向接口编程的带有动态特性的代码,一大堆的接口和ioc,有的时候不用调试方式跑起来看内存根本就不知道具体实例对应的是哪个类。
  整个过程由于入口到真正核心部分代码的分支可能会有很多,所以这是一个不断地探路、回溯的过程。推荐用一段大块、整段的时间去看代码,这样不至于下回看 
  的时候就忘记了。
  7First of all, you must have a clear understanding about the business logic, 
  then you can jump in the code.
  8
  1.jinq0123,supern lee--规范写代码,通过名称来了解类、变量、方法的含义。 
  2.Alan GAO--通过画UML图,了解软件结构。 
  3.云端孤鹜--通过类提供的接口,判断其是否重要,进而深入了解。 
  4.Michael--站在模块功能设计的角度上来了解,具体类里面先看私有变量,然后看接口。 
  5.sagasw--有本书叫code reading.找感兴趣的部分去读,然后外延,熟能生巧。读别人的设计文档或者看那些读代码的书。 
  6.机械唯物主义--逐个击破。 
  7.Weiming 
  Yin--(看法和我类似)根据软件的运行逻辑,从main开始看,不断延伸,通过打印信息跟踪观察,利用优秀编辑工具能够快速阅读代码。有些地方自己思考后发 现没有难度,可以粗略过,如果有些地方设计到多态或者分支,则需要研读,并延伸到它的数据结构。 
  8.Tiny fool--勇敢的移植代码学习者。 
  9.Kenny Yuan--规范和结构显得无比重要,大工程是无法掌握的。 
  10.Linker--看不懂代码的地方最好走人。(我相信你这么说是认为我们公司缺乏代码规范,不适合发展,其实也不是,就算公司再规范,也不是说就可以轻易 看懂代码的。还是要感谢你的关心,不过我会继续坚持下去的。)
  9
  最近刚好看了一个基于业务的C++面向对象的大型代码,说说我自己的感受吧
  代码量大概在5w行上下,全部是实现业务,底层封装全部有公司自己的基础类实现了,包括main()函数。。。
  我的习惯是首先把大的框架搞懂,明白这个代码的流程是怎样的,大块的功能是什么,如果直接看代码细节的话,会看的不知所以然,看完大体功能,大致就可以知道这个 程序是干嘛的了,然后再去钻研里面的细节。
  最重要的是做阅读笔记了,写写流程,把自己的理解写下来。
  大量的代码,看了肯定就会忘的,有笔记以后再复习就方便了 
  对于有大量基础实现的东西,可能不适应,我也没有这方面的经验。 打开源码我们可以看到 
  
  进入测试代码  ViewModelBaseTest ,我们知道他是通过以下 步骤来使用其消息机制的   void TestSimpleSendANDRevice()
  {
  Messenger.Reset();//把MESSAGER 单例 重置 为 空
  var vm = new TestViewModel();
  //注册
  //1接受 string 类型的 消息
  //2收到消息 时候 调用 HandleStringMessage
  Messenger.Default.Register(vm, vm.HandleStringMessage);
  const string Content1 = "Hello world";
  Messenger.Default.Send(Content1);//此时发送
  Assert.AreEqual(Content1, vm.ReceivedContent);
  }
  public class TestViewModel : ViewModelBase
  {
  string ReceivedContent ;
  internal void HandleStringMessage(string message)
  {
  ReceivedContent = message;
  }
  }
  那么他的底层又是如何实现的呢?
  让我们进入看看他的Messenger和其接口
  首先是其Register函数 
  
  /// 
  /// Registers a recipient for a type of message TMessage.
  /// 为T类型的参数 注册一个接受者,
  /// The action parameter will be executed when a corresponding (相应的)
  /// (当接收到) 参数action 这个方法将被执行
  /// message is sent. See the receiveDerivedMessagesToo parameter 
  /// for details on how messages deriving from TMessage (or, if TMessage is an interface,
  /// messages implementing TMessage) can be received too.
  /// 是否 接收  接口的 派生类的 信息
  /// Registering a recipient does not create a hard reference to it,
  /// so if this recipient is deleted, no memory leak is caused.
  /// 
  /// The type of message that the recipient registers
  /// for.
  /// The recipient that will receive the messages.
  /// A token(标记) for a messaging channel. 
  /// token 是一个 栏目的标记 
  /// If a recipient registers
  /// using a token, and a sender sends a message using the same token, then this
  /// 发送和接收方使用同一个标记 token接收 才能 接收到
  /// message will be delivered to the recipient. Other recipients who did not
  /// 
  /// use a token when registering (or who used a different token) will not
  /// get the message. 
  /// 
  /// Similarly, messages sent without any token, or with a different
  /// token, will not be delivered to that recipient.
  /// 
  /// If true, message types deriving from
  /// TMessage will also be transmitted to the recipient. For example, if a SendOrderMessage
  /// and an ExecuteOrderMessage derive from OrderMessage, registering for OrderMessage
  /// and setting receiveDerivedMessagesToo to true will send SendOrderMessage
  /// and ExecuteOrderMessage to the recipient that registered.
  /// 
  /// Also, if TMessage is an interface, message types implementing TMessage will also be
  /// transmitted to the recipient. For example, if a SendOrderMessage
  /// and an ExecuteOrderMessage implement IOrderMessage, registering for IOrderMessage
  /// and setting receiveDerivedMessagesToo to true will send SendOrderMessage
  /// and ExecuteOrderMessage to the recipient that registered.
  /// 
  /// The action that will be executed when a message
  /// of type TMessage is sent.
  public virtual void Register(//TMessage接收的 类型 
  object recipient,//接收的对象一般是viewmodel
  object token,//标记 ,细分类型下面的 分类。 
  bool receiveDerivedMessagesToo,// 是否也接收 (派生类的) 消息
  Action action) {//当有消息来临的时候的 回调函数
  var messageType = typeof(TMessage);
  Dictionary> recipients;
  //============================ 是否也接收 (派生类的) 消息
  if (receiveDerivedMessagesToo) {
  if (_recipientsOfSubclassesAction == null) {
  _recipientsOfSubclassesAction = new Dictionary>();
  }
  recipients = _recipientsOfSubclassesAction;
  }
  // 默认是 false -即 不接收 (派生类的) 消息
  else {
  if (_recipientsStrictAction == null) {
  _recipientsStrictAction = new Dictionary>();
  }
  recipients = _recipientsStrictAction;
  }
  //============================
  List list;
  if (!recipients.ContainsKey(messageType)) { //如果不存在 List 就添加
  list = new List();
  recipients.Add(messageType, list);
  } else {
  list = recipients[messageType];//存在就取出
  }
  // 新建一个  weakAction 弱引用 方法
  var weakAction = new WeakAction(recipient, action);// new WeakAction(viewmodel, function);
  var item = new WeakActionAndToken {
  Action = weakAction,
  Token = token
  };
  list.Add(item);
  //清除已经被垃圾回收的对象
  Cleanup();
  }
  简单说明其各参数的意思。
  public virtual void Register(//TMessage接收的 类型 
  object recipient,//接收的对象一般是viewmodel
  object token,//标记 ,细分类型下面的 分类。 
  bool receiveDerivedMessagesToo,// 是否也接收 (派生类的) 消息
  Action action) {//当有消息来临的时候的 回调函数
  }
  其实很多都是顾名思义的。不理解的话可以参考测试函数或进一步的分析来了解。
  我也是这样的。
  其他的代码都是可以自我解释的:创建一个静态的字典来存储注册信息。
  其中 引用了一个 WeakAction 类。
  正是这个关键的类 来实现了 对象调用函数,并且是弱引用的。 什么是弱引用? 如果应用程序的代码可以访问一个正由该程序使用的对象,垃圾回收器就不能收集该对象,那么,就认为应用程序对该对象具有强引用。 弱引用允许应用程序访问对象,同时也允许垃圾回收器收集相应的对象。如果不存在强引用,则弱引用的有限期只限于收集对象前的一个不确定的时间段。 
  弱引用使用的范围
  弱引用特别适合以下对象:占用大量内存,但通过垃圾回收功能回收以后很容易重新创建。  仅在必要时使用长弱引用,因为在终止后对象的状态是不可预知的。
  避免对小对象使用弱引用,因为指针本身可能和对象一样大,或者比对象还大。 不应将弱引用作为内存管理问题的自动解决方案,而应开发一个有效的缓存策略来处理应用程序的对象。 
  为什么要使用弱引用?
  当一个应用程序的VIEW非常多的时候,如果我们不得不为每个VIEW注册进全局消息订阅发布的一个机制里。但是有些页面可能并不是经常
  用到但因为通常的注册都是强类型的注册(直接添加VIEWMODEL进字典,发布机制必须持有一个VIEWMODEL的引用才能对其调用方法或改变属性),
  而不得回收。或许你可以通过令外一种思路,也许更好(比如使用调度器),但持有弱引用显然是个不错主意。
  如何使用? 
  这时候 让我们回到 MESSAGE的三方法之发送
  去到参数最多的那个函数 
  
  /// 
  /// 发送给目标 或者 类型 
  /// 
  /// 
  /// 
  /// 
  /// 
  private void SendToTargetOrType(TMessage message, Type messageTargetType, object token) {
  var messageType = typeof(TMessage);
  if (_recipientsOfSubclassesAction != null) {
  // Clone to protect from people registering in a "receive message" method
  // Bug correction Messaging BL0008.002
  var listClone = _recipientsOfSubclassesAction.Keys.Take(_recipient sOfSubclassesAction.Count()).ToList();
  foreach (var type in listClone) {
  List list = null;
  if (messageType == type
  || messageType.IsSubclassOf(type)
  || Implements(messageType, type)) {
  list = _recipientsOfSubclassesAction[type];
  }
  SendToList(message, list, messageTargetType, token);
  }
  }
  if (_recipientsStrictAction != null) {
  if (_recipientsStrictAction.ContainsKey(messageType)) {
  var list = _recipientsStrictAction[messageType];
  SendToList(message, list, messageTargetType, token);
  }
  }
  Cleanup();
  }
  然后我们追到 token) { 。。。。。。      executeAction.ExecuteWithObject(message); 。。。。。  }   最终实现的是  
  
  /// 
  /// Stores an Action without causing a hard reference to be created to the Action's owner.
  /// 为 拥有者 储存一个 方法 而不用 强引用
  /// The owner can be garbage collected at any time.
  /// 而使得 拥有者(viewmodel)能在任意时候被 回收
  /// 
  /// The type of the Action's parameter.
  ////[ClassInfo(typeof(Messenger))]
  public class WeakAction : WeakAction, IExecuteWithObject
  {
  private readonly Action _action;
  /// 
  /// Initializes a new instance of the WeakAction class.
  /// 
  /// The action's owner.
  /// The action that will be associated to this instance.
  public WeakAction(object target, Action action)
  : base(target, null)
  {
  _action = action;
  }
  /// 
  /// Gets the Action associated to this instance.
  /// 
  public new Action Action
  {
  get
  {
  return _action;
  }
  }
  /// 
  /// Executes the action. This only happens if the action's owner
  /// is still alive. The action's parameter is set to default(T).
  /// 
  public new void Execute()
  {
  if (_action != null
  && IsAlive)
  {
  _action(default(T));
  }
  }
  /// 
  /// Executes the action. This only happens if the action's owner
  /// is still alive.
  /// 
  /// A parameter to be passed to the action.
  public void Execute(T parameter)
  {
  if (_action != null
  && IsAlive)
  {
  _action(parameter);
  }
  }
  /// 
  /// Executes the action with a parameter of type object. This parameter
  /// will be casted to T. This method implements 
  /// and can be useful if you store multiple WeakAction{T} instances but don't know in advance
  /// what type T represents.
  /// 
  /// The parameter that will be passed to the action after
  /// being casted to T.
  public void ExecuteWithObject(object parameter)
  {
  var parameterCasted = (T) parameter;
  Execute(parameterCasted);
  }
  }
  这里牵扯到ACTION 和 T 的知识,不熟悉的可以自己去查一查。
  然后是取消订阅这个比较简单 就是一个遍历然后从原来的列表中删除。  /// 
  /// Unregisters a message recipient for a given type of messages and for
  /// a given action. Other message types will still be transmitted to the
  /// recipient (if it registered for them previously). Other actions that have
  /// been registered for the message type TMessage and for the given recipient (if
  /// available) will also remain available.
  /// 
  /// The type of messages that the recipient wants
  /// to unregister from.
  /// The recipient that must be unregistered.
  /// The action that must be unregistered for
  /// the recipient and for the message type TMessage.
  public virtual void Unregister(object recipient, Action action) {
  UnregisterFromLists(recipient, action, _recipientsStrictAction);
  UnregisterFromLists(recipient, action, _recipientsOfSubclassesAction);
  Cleanup();
  }
  提一下 Cleanup();
  这个函数的一个作用就是当GC回收了弱引用的对象,他就负责将其清除出列表。
  ViewModelBase
  他的VIEWMODEL只有一个。没有更多派生类。 
  其中几个就是下面。   IsInDesignMode 是否在设计模式之下 
  VerifyPropertyName 验证属性是否出错
  //封装了 消息的操作
  Broadcast  发送属性改变。即消息发送。
  继承自 ICleanup 接口的   Cleanup (); 取消
  // 
  其中不少思想都在John Gossman的文章中就有体现了。其中的RelayCommand就是直接在其文中就有。强推此文。
  其他的下次有时间再说。
分享到:
评论

相关推荐

    WPF MVVMLight框架使用源码,可运行看效果

    MVVMLight库由Laurent Bugnion开发,提供了许多辅助类和接口,如:ViewModelBase(提供INotifyPropertyChanged接口实现)、 Messenger(实现跨视图模型的消息传递)、 GalaSoft.MvvmLight.Command(封装ICommand...

    Mvvmlight消息通知源码及实例

    在给定的“Mvvmlight消息通知源码及实例”中,我们将探讨MvvMLight框架中的消息通知机制,它是如何工作以及如何在实际项目中应用。 MvvMLight的消息通知机制是通过一个名为`GalaSoft.MvvmLight.Messaging`的类库...

    mvvm light 消息机制

    总结起来,MVVM Light的消息机制提供了一种灵活、解耦的通信方式,使得ViewModel可以通过发送消息来驱动View的更新,而不必直接操作View组件。这种设计提高了代码的可读性和可维护性,也使得开发者能更容易地测试和...

    MVVMLight.zip

    - ** GalaSoft.MvvmLight.Messaging**: 一个消息总线,允许不同层之间的通信,不直接依赖于视图模型和视图。 - ** GalaSoft.MvvmLight.Command**: 扩展了命令的概念,使得在ViewModel中定义和执行命令变得简单。 - *...

    WPF+Mvvmlight 简单的导航实例

    同时,MvvmLight的其他特性如 Messenger(消息传递)和 GalaSoft.MvvmLight.Threading DispatcherHelper(线程辅助)也能进一步提升应用的可扩展性和性能。 总结来说,WPF+MvvmLight的导航实例展示了如何利用MVVM...

    MVVMLight页面跳转WPF

    MVVMLight提供了一个简单的接口,用于在View和ViewModel之间通信,如命令绑定、 Messenger(消息传递系统)和INavigationService(导航服务)。 在WPF中,页面跳转通常涉及窗口或用户控件的实例化和显示。使用MVVM...

    MvvmLight-Sample-master.zip

    1. **MvvmLight框架**:MvvmLight提供了一些基础类和接口,如`ViewModelBase`、` GalaSoft.MvvmLight.Command.RelayCommand`、` Messenger`等,它们简化了MVVM模式的实现。`ViewModelBase`包含`...

    各种版本的Galasoft.MvvmLight

    Galasoft.MvvmLight 的多版本设计确保了跨平台的兼容性和灵活性,无论是在传统的.NET桌面应用,还是现代的移动和UWP环境中,都能提供强大的MVVM支持。通过深入理解和熟练使用这个框架,开发者可以更高效地构建可维护...

    MVVMLight的消息发送工具源码,可单独使用

    分析MVVMLight的消息发送工具源码可以帮助理解其实现细节,例如消息队列的管理、线程安全的处理、消息订阅的存储以及如何将消息分发到订阅者等。 总之,MVVMLight的消息发送工具为MVVM架构提供了灵活且低耦合的...

    MvvmLight 简单demo

    3. ** Messenger**:MvvmLight的消息总线机制允许ViewModel之间相互通信,即使它们没有直接引用关系。这对于实现跨组件通信非常有用。 4. ** RelayCommand**:MvvmLight提供了RelayCommand,一个实现了ICommand接口...

    MVVMLight框架使用实例

    总结,MVVMLight通过数据绑定、消息传递和命令机制,简化了MVVM模式的实现,使得开发者可以更专注于业务逻辑,而不是繁琐的界面交互。在`MvvMLightLogin`实例中,我们看到了这些功能的实际应用,它们共同构成了一个...

    MVVM light小实例

    在 ViewModel 文件夹下,创建一个名为 `MainViewModel` 的类,继承自 MVVMLight 提供的 `ViewModelBase` 类。`ViewModelBase` 提供了 `RaisePropertyChanged` 方法,用于通知视图属性已更改。 **2. 视图模型** 在 ...

    GalaSoft.MvvmLight.WPF4

    GalaSoft.MvvmLight.WPF4和GalaSoft.MvvmLight.Extras.WPF4是两个关键的库,它们由法国开发者Laurent Bugnion创建,旨在简化MVVM模式的实现,提高开发效率。 GalaSoft.MvvmLight.WPF4是MVVMLight框架的核心部分,它...

    MvvmLight-NuGet

    **MvvmLight-NuGet** 是一个专门为开发者提供的NuGet包,它包含了MvvmLight Toolkit的集成,使得在.NET框架下开发WPF、Silverlight、Windows Phone和Universal Windows Platform(UWP)应用时,能够更加方便地应用...

    MVVMLight 框架 Page

    此外,MVVMLight还提供了`Messenger`服务,它是一种简单的消息传递机制,允许不同组件之间相互通信,即使它们没有直接的引用关系。这对于解耦组件和实现模块化设计非常有用。 总之,MVVMLight框架为C#开发者提供了...

    基于MvvmLight的MVVM程序内附详细说明.7z

    一个 基于 MVVM LIGHT程序实例,内附详细的教程。包括1.1 MVVM Light Toolkit下载,安装 目录 1 MVVM Light的安装 1 1.1 MVVM Light Toolkit下载 1 1.2 MVVM Light Toolkit安装 2 2 程序实例 3 2.1 建立工程文件 3 ...

    WPF-MvvmLight

    2. ** GalaSoft.MvvmLight Messenger**: 一个消息传递系统,允许不同层之间进行松散耦合的通信,无需直接引用彼此。 3. ** GalaSoft.MvvmLight.Command**: 提供了基于委托的命令实现,使得在ViewModel中定义和执行...

    MVVMLight框架下的wpf登陆程序demo

    3. 使用GalaSoft.MvvmLight.Messaging.Messenger实例发送和接收消息,通知登录状态。 4. 在XAML中绑定视图模型的属性,并设置EventToCommand行为。 5. 根据登录验证结果决定显示哪个窗口。 总结,MVVMLight框架简化...

    MEF 结合 MVVMLight

    2. ** Messenger服务**:MVVMLight包含一个消息传递机制,允许ViewModel之间进行通信,而无需直接引用彼此,增加了代码的灵活性。 3. ** GalaSoft.MvvmLight.Command**:提供了一套命令实现,简化了视图模型中的命令...

    MVVM(GalaSoft.MvvmLight)简单小项目和其它MVVM框架(CommunityToolkit.Mvvm)

    MVVM(GalaSoft.MvvmLight)简单小项目和其它MVVM框架(CommunityToolkit.Mvvm)

Global site tag (gtag.js) - Google Analytics