MVVM Mediator 模式






4.96/5 (18投票s)
MVVM Mediator 模式。
大约一年前,我的好朋友 Marlon Grech 写了一篇关于 MVC + M 的精彩文章。你可以在这里阅读 Marlon 的优秀文章。
本质上,Marlon 所做的是创建了一个消息系统,以便不同的 MVC 类可以相互通信。Marlon 的原始代码非常出色,但我只是不喜欢他使用 string
来传递消息的方式,除此之外,我的代码与他的代码几乎没有变化。所以,Marlon 干得好,你真是个高手。
无论如何,Marlon 最初文章背后的想法是使用 MVC 模式,并结合一个名为 Mediator
的附加类,该类了解消息以及在发生需要发送消息的事情时通知谁。
如今,大多数人会在开发 WPF 时使用 MVVM 模式。我有一个小型的演示应用程序,它执行以下操作。
- 它有 2 个
textbox
,其中一个是可以写入的textbox
,用于通过Mediator
发送消息。 - 第二个
textbox
通过第一个textbox
更改时通过Mediator
发送的消息进行刷新。
让我们看一些代码吧。
首先,一个辅助类(从 Marlon 的博客上借鉴而来)。
1: using System;
2: using System.Collections.Generic;
3: using System.Linq;
4: using System.Text;
5:
6: namespace MediatorDemo
7: {
8: /// <summary>
9: /// The multi dictionary is a dictionary that contains
10: /// more than one value per key
11: /// </summary>
12: /// <typeparam name="T">The type of the key</typeparam>
13: /// <typeparam name="K">The type of the list contents</typeparam>
14: public class MultiDictionary<T, K>
15: : Dictionary<T, List<K>>
16: {
17:
18: #region Private Methods
19: //checks if the key is already present
20: private void EnsureKey(T key)
21: {
22: if (!ContainsKey(key))
23: {
24: this[key] = new List<K>(1);
25: }
26: else
27: {
28: if (this[key] == null)
29: this[key] = new List<K>(1);
30: }
31: }
32: #endregion
33:
34: #region Public Methods
35: /// <summary>
36: /// Adds a new value in the Values collection
37: /// </summary>
38: /// <param name="key">The key where to place the
39: /// item in the value list</param>
40: /// <param name="newItem">The new item to add</param>
41: public void AddValue(T key, K newItem)
42: {
43: EnsureKey(key);
44: this[key].Add(newItem);
45: }
46:
47:
48: /// <summary>
49: /// Adds a list of values to append to the value collection
50: /// </summary>
51: /// <param name="key">The key where to place the item in the value list</param>
52: /// <param name="newItems">The new items to add</param>
53: public void AddValues(T key, IEnumerable<K> newItems)
54: {
55: EnsureKey(key);
56: this[key].AddRange(newItems);
57: }
58:
59: /// <summary>
60: /// Removes a specific element from the dict
61: /// If the value list is empty the key is removed from the dict
62: /// </summary>
63: /// <param name="key">The key from where to remove the value</param>
64: /// <param name="value">The value to remove</param>
65: /// <returns>Returns false if the key was not found</returns>
66: public bool RemoveValue(T key, K value)
67: {
68: if (!ContainsKey(key))
69: return false;
70:
71: this[key].Remove(value);
72:
73: if (this[key].Count == 0)
74: this.Remove(key);
75:
76: return true;
77: }
78:
79: /// <summary>
80: /// Removes all items that match the prediacte
81: /// If the value list is empty the key is removed from the dict
82: /// </summary>
83: /// <param name="key">The key from where to remove the value</param>
84: /// <param name="match">The predicate to match the items</param>
85: /// <returns>Returns false if the key was not found</returns>
86: public bool RemoveAllValue(T key, Predicate<K> match)
87: {
88: if (!ContainsKey(key))
89: return false;
90:
91: this[key].RemoveAll(match);
92:
93: if (this[key].Count == 0)
94: this.Remove(key);
95:
96: return true;
97: }
98: #endregion
99: }
100: }
这只是允许为特定消息注册多个对象。
接下来是 Mediator
,它是一个单例,并且知道如何针对回调发送和注册消息,这正是我修改的地方。在 Marlon 的原始代码中,他使用了 String
,而我现在使用 Action<Object> delegate
作为回调。我认为这是一个小小的改进。
1: using System;
2: using System.Collections.Generic;
3: using System.Linq;
4: using System.Text;
5:
6: namespace MediatorDemo
7: {
8: /// <summary>
9: /// Available cross ViewModel messages
10: /// </summary>
11: public enum ViewModelMessages { UserWroteSomething = 1 };
12:
13:
14: public sealed class Mediator
15: {
16: #region Data
17: static readonly Mediator instance = new Mediator();
18: private volatile object locker = new object();
19:
20: MultiDictionary<ViewModelMessages, Action<Object>> internalList
21: = new MultiDictionary<ViewModelMessages, Action<Object>>();
22: #endregion
23:
24: #region Ctor
25: //CTORs
26: static Mediator()
27: {
28:
29:
30: }
31:
32: private Mediator()
33: {
34:
35: }
36: #endregion
37:
38: #region Public Properties
39:
40: /// <summary>
41: /// The singleton instance
42: /// </summary>
43: public static Mediator Instance
44: {
45: get
46: {
47: return instance;
48: }
49: }
50:
51: #endregion
52:
53: #region Public Methods
54: /// <summary>
55: /// Registers a Colleague to a specific message
56: /// </summary>
57: /// <param name="callback">The callback to use
58: /// when the message it seen</param>
59: /// <param name="message">The message to
60: /// register to</param>
61: public void Register(Action<Object> callback,
62: ViewModelMessages message)
63: {
64: internalList.AddValue(message, callback);
65: }
66:
67:
68: /// <summary>
69: /// Notify all colleagues that are registered to the
70: /// specific message
71: /// </summary>
72: /// <param name="message">The message for the notify by</param>
73: /// <param name="args">The arguments for the message</param>
74: public void NotifyColleagues(ViewModelMessages message,
75: object args)
76: {
77: if (internalList.ContainsKey(message))
78: {
79: //forward the message to all listeners
80: foreach (Action<object> callback in
81: internalList[message])
82: callback(args);
83: }
84: }
85: #endregion
86:
87: }
88: }
那么它是如何工作的呢?让我们看一个通过中介者发送消息的 ViewModel
,请注意下面 NotifyColleagues(..)
方法的使用。
1: using System;
2: using System.Collections.Generic;
3: using System.ComponentModel;
4: using System.Linq;
5: using System.Text;
6: using System.Windows.Input;
7: using System.Windows.Data;
8:
9:
10: namespace MediatorDemo
11: {
12: public class WritersViewModel : ViewModelBase
13: {
14: private String writerText = null;
15:
16:
17: public WritersViewModel()
18: {
19:
20:
21: }
22:
23: public String WriterText
24: {
25: get { return writerText; }
26: set
27: {
28: writerText = value;
29: NotifyChanged("WriterText");
30: //alert others about this change
31: //via Mediator
32: Mediator.Instance.NotifyColleagues(
33: ViewModelMessages.UserWroteSomething,
34: writerText);
35: }
36: }
37:
38: }
39: }
如何处理来自 Mediator
的消息呢?这很简单,尽管我们需要将 Object
的结果转换为我们期望的 Type
。这是必要的,因为 Mediator
使用 Action<Object>
委托作为回调,其中 Object
当然可以是任何 Type
。
1: using System;
2: using System.Collections.Generic;
3: using System.ComponentModel;
4: using System.Linq;
5: using System.Text;
6: using System.Windows.Input;
7:
8:
9: namespace MediatorDemo
10: {
11: public class ReadersViewModel : ViewModelBase
12: {
13: private String readText = String.Empty;
14:
15:
16: public ReadersViewModel()
17: {
18: //register to the mediator for the
19: //UserWroteSomething message
20: Mediator.Instance.Register(
21:
22: //Callback delegate, when message is seen
23: (Object o) =>
24: {
25: ReadText = (String)o;
26: }, ViewModelMessages.UserWroteSomething);
27: }
28:
29:
30: public String ReadText
31: {
32: get { return readText; }
33: set
34: {
35: readText = value;
36: NotifyChanged("ReadText");
37: }
38: }
39: }
40: }
这真的就是全部了。你现在有一个 Writer ViewModel
,它将通过 Mediator
/Action<Object> delegate
回调通知一个完全断开连接的 Reader ViewModel
发生了更改,这些回调已由希望基于消息进行操作的类注册到 Mediator
。
正如我所说,这都要归功于 Marlon,Marlon 干得好!
还有 这里有一个小型的演示应用程序,供你玩耍。