MVVM Mediator Pattern
About 1 year ago a good friend of mine Marlon Grech wrote a lovely article on MVC + M. You can read all about Marlons great article over at http://marlongrech.wordpress.com/2008/03/20/more-than-just-mvc-for-wpf/
Essentially what Marlon did was create a message system to allow disparate MVC classes to communicate with each other. The original code by Marlon was spot on, but I just didn’t like the way he was using strings for his messages, but other than that there is very little change from my code here to his code. So well done Marlon, you are ace.
Anyway the idea behind Marlons original post was using the MVC pattern along with an additional class called the Mediator whom knew about messages and who to notify when something happened that warrants a message being sent out.
These days most folk will use the MVVM pattern when developing WPF. I have a small demo app that does the following.
- It has 2 textboxes, one of which is a writable textbox, which is used to send messages via the Mediator
- The 2nd textbox is refreshed via the message that is sent via the Mediator when the 1st textbox changes
Lets have a look at some code:
1st a helper class (lifted from Marlons blog)
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: }
This simply allows more than 1 object to be registered for a particular message
Next comes the Mediator which is a singleton, and know how to send and register messages against callback, which is what I have changed. In Marlons original code he used Strings, where as I am now using Action<Object> delegates as callbacks. A minor improvement I feel.
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 registed 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: }
So how does this work, well lets see a ViewModel that sends a message via the mediator, notice the NotifyColleagues(..) method usage below.
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: }
And how about dealing with a message from the Mediator, this is fairly simple, though we do have to cast the results from Object to the Type we are expecting. This is necessary as the Mediator uses Action<Object> delegates as callbacks, where Object could be any Type of course.
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: }
And that is really all there is to it. You now have a Writer ViewModel that will notify a totally disconnected Reader ViewModel about a change, via the messaging providing by the Mediator/Action<Object> delegate callbacks, that were registered for the message by the class that wants to do something based on the message being registered with the Mediator.
As I say this is all thanks to Marlon, well done Marlon.
And here is a small demo app for you to play with


























Raul Mainardi Neto said
am March 16 2009 @ 11:33 pm
o.O nice stuff… seen the MVC post before, nice use of it.. you study it a little deeper… Thank for sharing Sacha
All best mate
Raul
sacha said
am March 17 2009 @ 10:42 am
Cheers Raul
Pramod.P.V said
am March 17 2009 @ 11:52 am
Nice.. and as usual.. the use of Action was good.
sacha said
am March 17 2009 @ 12:35 pm
Cheers man
Dew Drop - March 17, 2009 | Alvin Ashcraft's Morning Dew said
am March 17 2009 @ 12:48 pm
[...] MVVM Mediator Pattern (Sacha Barber) [...]
Ted said
am March 17 2009 @ 2:34 pm
Cool, I like it! Now you should just implement so that the mediator could be configured on what thread to dispatch each message, individual listeners might want the message on different threads… maybe auto-hookup on the dispatcher that was used when registering to listen for a message, or as a parameter maybe… Just an idea…
Cheers!
sacha said
am March 17 2009 @ 2:57 pm
Ted
Great idea, I may look into that at some point.
Paul said
am March 17 2009 @ 7:09 pm
Have you taken a look at http://www.codeplex.com/CompositeWpF?
They have a very nice implementation of Pub Sub Messaging within WPF (their Event Aggregator). Why re-invent something that is already there and works very very well?
I am not trying to downplay what you have done, because it is always good to see alternate implementations, just wasnt sure if you had seen their work. If you are heading down a messaging path, the work they have done has a lot of stuff that you will eventually try to implement.
sacha said
am March 17 2009 @ 8:17 pm
Paul
Thanks for that, and yes yes I have heard and worked with PRISM aka Composit WPF, which in my mind is code bloat, and should not be used unless you have a very large project. It reminds me of the Smart Client Software Factory which we are in the process of ripping out.
I just dont like heavy frameworks, and PRISM I am afraid to say is one such framework.
Mladen Mihajlovic said
am March 17 2009 @ 8:23 pm
The reason why the mediator pattern usually uses a string and not an enum is that every time you want to add a new message type, you have to modify the enum. With a string it is far more flexible.
Paul said
am March 17 2009 @ 8:51 pm
Sorry to hear your opinion of Prism. I agree that CAB was huge, but Prism has taken a much different approach.
Even if you dont want to use the framework, it would be worth your while IMO, to take a look at the source for the event aggregator. They handle the case you were talking about above ( UI Thread, etc. ). It really has some neat concepts in it.
Most of the work I do ends up needing the things that Prism provides. So I look at something like Prism from a completely different viewpoint.
sacha said
am March 18 2009 @ 8:34 am
I hear you, I think it is ok to take stuff from PRISM, but not use the whole lot together, its way to much IMHO.
But agreed there are “some good parts” to it.
sacha said
am March 18 2009 @ 8:35 am
Mladen
Fair ewnough, I still would prefer to use enums as I will know what messages I want to respond to in advance and I do not want to add any more dynamically at runtime, so its is fine with enums for my purposes
tawani said
am March 18 2009 @ 3:10 pm
Nice article but you should change the property names of the MultiDictionary to reflect the IDictionary property names (Add, AddRange, Remove, RemoveAll instead of AddValue, AddValues, RemoveValue, RemoveAllValues).
Also change “EnsureKey” to “EnsureKeyExists” and “NotifyColleagues” to “NotifySubscribers”
sacha said
am March 18 2009 @ 4:13 pm
Like I say I lifted that from Marlons post. But yeah you are right.
sachabarber.net » WPF : More Mediator Goodness said
am April 14 2009 @ 7:37 am
[...] while back I wrote a post about using the Mediator pattern, to allow ViewModels to communicate with each other easily and [...]
Jose said
am May 27 2009 @ 4:47 pm
The question I have with this is that to have an enumeration in the mediator class means that it can’t really be reused. You will have to add the Mediator code files into every project you use and populate the enumeration. Not a big deal, it’s just copy and paste. But can you think of a way to make the mediator generic so the application chooses the enumeration values?
sacha said
am May 27 2009 @ 8:07 pm
If you can wait a while I am working on rather cool MVVM framework with much better Mediator in it also.
nitzzzu said
am May 31 2009 @ 8:46 am
Hi Sacha. The mediator pattern is great. But you can only broadcast messages. Let’s say I have 3 modules in my super cool LOB application: Module1, Module2, Module3. Module1 has a list of customers (some of them are selected) and it’s bound to Module1ViewModel. Module2 and Module3 both subscribe to ReceivedSelectedCustomers message (this message will be published by Module1). At a certain time we want to get the selected customers from Module2 only and we sendmessage(Module1.GetSelectedCustomers). Module1 receives the message and wants to send back a reply (ReceivedSelectedCustomers). The problem is that we don’t have the sender of Module1.GetSelectedCustomers because the message was broadcasted. Is there an elegant way to solve this? thanks
sacha said
am May 31 2009 @ 4:02 pm
nitzzzu
What you say is true, it is a broadcast mechanism, perhaps PRISMs event Aggregator would suit your needs better