MVVMLight源码分析之消息机制和ViewModelBase
2010-11-06 18:24 撞破南墙 阅读(7759) 评论(7) 编辑 收藏 举报目录
1介绍MVVMLight
试用范围: Windows Presentation Foundation, Silverlight and for Windows Phone 7
作者的介绍
- The GalaSoft.MvvmLight library with helper classes:
- A ViewModelBase class to be used as the base class for ViewModels.
- A Messenger class (and diverse message types) to be used to communicate within the application. Recipients only receive the message types that they register for. Additionally, a target type can be specified, in which case the message will only be transmitted if the recipient's type matches the target parameter.
Messages can be anything from simple values to complex objects. You can also use specialized message types, or create your own types deriving from them.
More information about the Messenger class.- MessageBase: A simple message class, carrying optional information about the message's sender.
- GenericMessage<T>: 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<T>: 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<T>: 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<T>: 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<T> 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代码分析方法
2.1 按照骑士的建议,先从测试代码看起。完整了解其功能。
2.2 使用类图看其结构。
3.3 顾名思义+debug看其执行。
之前我还去TOPLANGUAGE整理了一下牛牛们的想法,也一并贴出来供大家参考。排版乱抱歉。
3具体剖析
打开源码我们可以看到
进入测试代码
3.1 消息机制的剖析
ViewModelBaseTest ,我们知道他是通过以下 步骤来使用其消息机制的
{
Messenger.Reset();//把MESSAGER 单例 重置 为 空
var vm = new TestViewModel();
//注册
//1接受 string 类型的 消息
//2收到消息 时候 调用 HandleStringMessage
Messenger.Default.Register<string>(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.
/// 是否 接收 接口的 派生类的 信息
/// <para>Registering a recipient does not create a hard reference to it,
/// so if this recipient is deleted, no memory leak is caused.</para>
/// </summary>
/// <typeparam name="TMessage">The type of message that the recipient registers
/// for.</typeparam>
/// <param name="recipient">The recipient that will receive the messages.</param>
/// <param name="token">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.</param>
///
/// <param name="receiveDerivedMessagesToo">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.
///
/// <para>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.</para>
/// </param>
/// <param name="action">The action that will be executed when a message
/// of type TMessage is sent.</param>
public virtual void Register<TMessage>(//TMessage接收的 类型
object recipient,//接收的对象一般是viewmodel
object token,//标记 ,细分类型下面的 分类。
bool receiveDerivedMessagesToo,// 是否也接收 (派生类的) 消息
Action<TMessage> action) {//当有消息来临的时候的 回调函数
var messageType = typeof(TMessage);
Dictionary<Type, List<WeakActionAndToken>> recipients;
//============================ 是否也接收 (派生类的) 消息
if (receiveDerivedMessagesToo) {
if (_recipientsOfSubclassesAction == null) {
_recipientsOfSubclassesAction = new Dictionary<Type, List<WeakActionAndToken>>();
}
recipients = _recipientsOfSubclassesAction;
}
// 默认是 false -即 不接收 (派生类的) 消息
else {
if (_recipientsStrictAction == null) {
_recipientsStrictAction = new Dictionary<Type, List<WeakActionAndToken>>();
}
recipients = _recipientsStrictAction;
}
//============================
List<WeakActionAndToken> list;
if (!recipients.ContainsKey(messageType)) { //如果不存在 List 就添加
list = new List<WeakActionAndToken>();
recipients.Add(messageType, list);
} else {
list = recipients[messageType];//存在就取出
}
// 新建一个 weakAction 弱引用 方法
var weakAction = new WeakAction<TMessage>(recipient, action);// new WeakAction<TMessage>(viewmodel, function);
var item = new WeakActionAndToken {
Action = weakAction,
Token = token
};
list.Add(item);
//清除已经被垃圾回收的对象
Cleanup();
}
简单说明其各参数的意思。
public virtual void Register<TMessage>(//TMessage接收的 类型
object recipient,//接收的对象一般是viewmodel
object token,//标记 ,细分类型下面的 分类。
bool receiveDerivedMessagesToo,// 是否也接收 (派生类的) 消息
Action<TMessage> action) {//当有消息来临的时候的 回调函数
}
其实很多都是顾名思义的。不理解的话可以参考测试函数或进一步的分析来了解。
我也是这样的。
其他的代码都是可以自我解释的:创建一个静态的字典来存储注册信息。
其中 引用了一个 WeakAction 类。
正是这个关键的类 来实现了 对象调用函数,并且是弱引用的。
为什么要使用弱引用?
当一个应用程序的VIEW非常多的时候,如果我们不得不为每个VIEW注册进全局消息订阅发布的一个机制里。但是有些页面可能并不是经常
用到但因为通常的注册都是强类型的注册(直接添加VIEWMODEL进字典,发布机制必须持有一个VIEWMODEL的引用才能对其调用方法或改变属性),
而不得回收。或许你可以通过令外一种思路,也许更好(比如使用调度器),但持有弱引用显然是个不错主意。
如何使用?
这时候 让我们回到 MESSAGE的三方法之发送
去到参数最多的那个函数
/// 发送给目标 或者 类型
/// </summary>
/// <typeparam name="TMessage"></typeparam>
/// <param name="message"></param>
/// <param name="messageTargetType"></param>
/// <param name="token"></param>
private void SendToTargetOrType<TMessage>(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(_recipientsOfSubclassesAction.Count()).ToList();
foreach (var type in listClone) {
List<WeakActionAndToken> 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();
}
然后我们追到
TMessage message,
IEnumerable<WeakActionAndToken> list,
Type messageTargetType,
object
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)能在任意时候被 回收
/// </summary>
/// <typeparam name="T">The type of the Action's parameter.</typeparam>
////[ClassInfo(typeof(Messenger))]
public class WeakAction<T> : WeakAction, IExecuteWithObject
{
private readonly Action<T> _action;
/// <summary>
/// Initializes a new instance of the WeakAction class.
/// </summary>
/// <param name="target">The action's owner.</param>
/// <param name="action">The action that will be associated to this instance.</param>
public WeakAction(object target, Action<T> action)
: base(target, null)
{
_action = action;
}
/// <summary>
/// Gets the Action associated to this instance.
/// </summary>
public new Action<T> Action
{
get
{
return _action;
}
}
/// <summary>
/// Executes the action. This only happens if the action's owner
/// is still alive. The action's parameter is set to default(T).
/// </summary>
public new void Execute()
{
if (_action != null
&& IsAlive)
{
_action(default(T));
}
}
/// <summary>
/// Executes the action. This only happens if the action's owner
/// is still alive.
/// </summary>
/// <param name="parameter">A parameter to be passed to the action.</param>
public void Execute(T parameter)
{
if (_action != null
&& IsAlive)
{
_action(parameter);
}
}
/// <summary>
/// Executes the action with a parameter of type object. This parameter
/// will be casted to T. This method implements <see cref="IExecuteWithObject.ExecuteWithObject" />
/// and can be useful if you store multiple WeakAction{T} instances but don't know in advance
/// what type T represents.
/// </summary>
/// <param name="parameter">The parameter that will be passed to the action after
/// being casted to T.</param>
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.
/// </summary>
/// <typeparam name="TMessage">The type of messages that the recipient wants
/// to unregister from.</typeparam>
/// <param name="recipient">The recipient that must be unregistered.</param>
/// <param name="action">The action that must be unregistered for
/// the recipient and for the message type TMessage.</param>
public virtual void Unregister<TMessage>(object recipient, Action<TMessage> action) {
UnregisterFromLists(recipient, action, _recipientsStrictAction);
UnregisterFromLists(recipient, action, _recipientsOfSubclassesAction);
Cleanup();
}
提一下 Cleanup();
这个函数的一个作用就是当GC回收了弱引用的对象,他就负责将其清除出列表。
ViewModelBase
他的VIEWMODEL只有一个。没有更多派生类。
其中几个就是下面。
其中不少思想都在John Gossman的文章中就有体现了。其中的RelayCommand就是直接在其文中就有。强推此文。
其他的下次有时间再说。
作者:撞破南墙
出处:http://www.cnblogs.com/facingwaller/
关于作者:本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。