代码改变世界

MvvmLight的Message使用

2012-12-16 21:16  爱研究源码的javaer  阅读(1401)  评论(0编辑  收藏  举报

MvvmLight 的消息系统是比较强大的,我们通过使用它来进一步了解它内部的实现。之前Mgen的这篇文章已经介绍过它的一些用法。

我们先定义基于ViewModelBase的类,暂且叫他TestPorpertyChangedViewModel:

public class TestPorpertyChangedViewModel:ViewModelBase
        {
            public TestPorpertyChangedViewModel()
            {
                Messenger.Default.Register<PropertyChangedMessage<string>>(this, msg => DebugInfo(msg));

                RaisePropertyChanged("Prop", "John", "Zhang", true);//调用上面注册的msg => PrintInfo(msg);
            }

            public void DebugInfo(object obj)
            {
                var str = string.Empty;
                foreach (var prop in obj.GetType().GetProperties())
                {
                    str += string.Format("{0}: {1}{2}", prop, prop.GetValue(obj, null), Environment.NewLine);
                }
                Debug.WriteLine("========");
                Debug.WriteLine(str);
            }
            public const string PropPropertyName = "Prop";

            private string _prop;
            public string Prop
            {
                get
                {
                    return _prop;
                }

                set
                {
                    if (_prop == value)
                    {
                        return;
                    }

                    var oldValue = _prop;
                    _prop = value;
                    //RaisePropertyChanged(PropPropertyName, oldValue, value, true);
                }
            }
        }

为了测试方便,我们在构造函数里调用了 Messager的注册操作,并且执行消息,其实就是调用ViewModelBase的BroadCast方法。

                Messenger.Default.Register<PropertyChangedMessage<string>>(this, msg => DebugInfo(msg));

                RaisePropertyChanged("Prop", "John", "Zhang", true);//调用上面注册的msg => PrintInfo(msg);

然后我们就可以写测试代码了.就一句代码搞定:

[TestMethod]
public void TestPropertyChangedMsg()
{
    TestPorpertyChangedViewModel test = new TestPorpertyChangedViewModel();
}
执行上述代码就可以在调试窗口里看见:

image

接下来我们就来分析下MvvmLight到底调用了哪些方法呢?

首先就是调用IMessager类的Register函数:

void Register<TMessage>(object recipient, Action<TMessage> action);

Messager类里是如此实现的:

public virtual void Register<TMessage>(object recipient, Action<TMessage> action)
{
    Register(recipient, null, false, action);
}
public virtual void Register<TMessage>(//TMessage接受的类型
    object recipient,//接收的对象一般是viewmodel
    object token,//标记,细分类型下面的分类
    bool receiveDerivedMessagesToo,//是否也接收 (派生类)消息
    Action<TMessage> action)//当有消息来的时候 回调函数
{
    lock (_registerLock)//锁住_registerLock,多线程锁
    {
        Type messageType = typeof(TMessage);//消息类型

        Dictionary<Type, List<WeakActionAndToken>> recipients;//定义存储容器recipients

        if (receiveDerivedMessagesToo)//判断是否接受继承的消息
        {
            if (_recipientsOfSubclassesAction == null)
            {
                _recipientsOfSubclassesAction = new Dictionary<Type, List<WeakActionAndToken>>();
            }

            recipients = _recipientsOfSubclassesAction;//把继承的消息字典附给recipients原始容器
        }
        else
        {
            if (_recipientsStrictAction == null)
            {
                _recipientsStrictAction = new Dictionary<Type, List<WeakActionAndToken>>();
            }

            recipients = _recipientsStrictAction;
        }

        lock (recipients)//锁住
        {
            List<WeakActionAndToken> list;

            if (!recipients.ContainsKey(messageType))//判断容器中是否包含messageType
            {
                list = new List<WeakActionAndToken>();//定义新的WeakActionAndToken集合
                recipients.Add(messageType, list);//不包含的话添加
            }
            else
            {
                list = recipients[messageType];//把该messageType的Value附给list
            }

            var weakAction = new WeakAction<TMessage>(recipient, action);

            var item = new WeakActionAndToken
            {
                Action = weakAction,//weakAction附给weakActionAndToken
                Token = token
            };

            list.Add(item);//把item添加进list集合
        }
    }

    RequestCleanup();//请求清除资源。
}

其实说的简单点就是添加进集合,接下来我们看看RaisePropertyChanged函数,就是去调用它的weakAction了。

protected virtual void Broadcast<T>(T oldValue, T newValue, string propertyName)
{
    var message = new PropertyChangedMessage<T>(this, oldValue, newValue, propertyName);
    MessengerInstance.Send(message);
}

我们看到其实也就是调了MessageInstance.Send(message)方法啊。

private void SendToTargetOrType<TMessage>(TMessage message, Type messageTargetType, object token)
{
    Type messageType = typeof(TMessage);

    if (_recipientsOfSubclassesAction != null)
    {
        // Clone to protect from people registering in a "receive message" method
        // Correction Messaging BL0008.002
        List<Type> listClone =
            _recipientsOfSubclassesAction.Keys.Take(_recipientsOfSubclassesAction.Count()).ToList();

        foreach (Type type in listClone)
        {
            List<WeakActionAndToken> list = null;

            if (messageType == type
                || messageType.IsSubclassOf(type)
                || type.IsAssignableFrom(messageType))
            {
                lock (_recipientsOfSubclassesAction)
                {
                    list = _recipientsOfSubclassesAction[type].Take(_recipientsOfSubclassesAction[type].Count()).ToList();
                }
            }

            SendToList(message, list, messageTargetType, token);
        }
    }

    if (_recipientsStrictAction != null)
    {
        lock (_recipientsStrictAction)
        {
            if (_recipientsStrictAction.ContainsKey(messageType))
            {
                var list = _recipientsStrictAction[messageType]
                    .Take(_recipientsStrictAction[messageType].Count())
                    .ToList();

                SendToList(message, list, messageTargetType, token);
            }
        }
    }

    RequestCleanup();
}

因为我们在注册消息的时候是使用默认的不注册派生类的所以实际我们调用的是:

if (_recipientsStrictAction != null)
{
    lock (_recipientsStrictAction)
    {
        if (_recipientsStrictAction.ContainsKey(messageType))
        {
            var list = _recipientsStrictAction[messageType]
                .Take(_recipientsStrictAction[messageType].Count())
                .ToList();

            SendToList(message, list, messageTargetType, token);
        }
    }
}
看看SendToList这个静态私有方法:
private static void SendToList<TMessage>(
    TMessage message,
    IEnumerable<WeakActionAndToken> list,
    Type messageTargetType,
    object token)
{
    if (list != null)
    {
        // Clone to protect from people registering in a "receive message" method
        // Correction Messaging BL0004.007
        List<WeakActionAndToken> listClone = list.Take(list.Count()).ToList();

        foreach (WeakActionAndToken item in listClone)
        {
            var executeAction = item.Action as IExecuteWithObject;

            if (executeAction != null
                && item.Action.IsAlive
                && item.Action.Target != null
                && (messageTargetType == null
                    || item.Action.Target.GetType() == messageTargetType
                    || messageTargetType.IsAssignableFrom(item.Action.Target.GetType()))
                && ((item.Token == null && token == null)
                    || item.Token != null && item.Token.Equals(token)))
            {
                executeAction.ExecuteWithObject(message);
            }
        }
    }
}
其实也就是遍历调用了WeakAction泛型类的ExecuteWithObject方法,里面也就是调用了我们定义的委托。
关键就是遍历消息类型,找到这个注册的PropertyChangedMessage。

下篇我们详细剖析下各个方法的实现。