Mediator v2 for MVVM WPF and Silverlight applications
This last 2 weeks, us WPF Disciples have been talking a lot about the Mediator pattern for MVVM applications. Me and Josh Smith revised my previous Mediator implementation and found some problems with it.
Problem: There was a memory leak. Once a Mediator subscribes for one or more messages it would be kept in memory since the Mediator was holding a reference to the ViewModel.
Solution: We could have just exposed an unregister method so that when you call this method the Mediator would release the instance of the ViewModel. But sometimes it is hard to know when a ViewModel is going to die… So we went a step further and created the WeakAction. The WeakAction stores the instance of the ViewModel in a WeakRefence thus the ViewModel can be garbage collected without any problem and once it does get garbage collected the Mediator automatically removes that ViewModel registration from the list.
While we were at it we PIMPED the Mediator API
We added 2 approaches to subscribe to the Mediator and we also added support for (thanks to David Anson suggestion) strongly typed parameters.
So now the Mediator offers 2 methods of registration, a declarative way of registering and an imperative way as well.
The Declarative approach. (Supported in WPF and Silverlight)
This approach uses attributes so that you can decorate the messages that act as subscribers for messages.Here is an example
1: /// <summary>
2: /// Subscribe to Message1
3: /// </summary>
4: /// <param name="message">The message to be passed</param>
5: [MediatorMessageSink(MediatorMessages.Message1, ParameterType = typeof(SomeData))]
6: public void Test(SomeData data)
7: {
8: MessageBox.Show(data.Text + "\nReceived by: ColleagueA");
9: }
So you just have to decorate your method with MediatorMessageSink attribute and the Method will get invoked every time a message is broadcasted via the Mediator.
As you can see the Mediator also supports strongly typed parameters. In order to do so you have to specify the ParameterType property on the attribute as shown in the sample code above. You can pass 0 or 1 parameters. If you don’t have any parameters just ignore the ParameterType (since this is an named Property and you can just not set it )
When using the declarative approach you must register the instance of the ViewModel to the Mediator. This is just one line of code in the ViewModel constructor. Here is how you do that
1: public ColleagueA()
2: {
3: //Register all decorated methods to the Mediator
4: Mediator.Register(this);
5: }
This will inspect all methods of the ViewModel and look for the MediatorMessageSink attribute and register the appropriate methods to the Mediator. If you are going to have a base class for the ViewModels in your project and you can put this in the constructor of your base class. As simple an that.
The Imperative approach. (Supported in WPF ONLY)
If you do not want to use the attribute based registration we offer an imperative approach where you specify a method to be registered to a specific message. This implementation is exactly as it was in the old Mediator yet now it supports strongly typed parameters.
Here is how you can use this in your code
1: public ColleagueC()
2: {
3: //Register a specific delegate to the Mediator
4: Mediator.Register(MediatorMessages.Message1,
5: (Action<SomeData>)
6: delegate(SomeData x)
7: {
8: Test = x.Text;
9: MessageBox.Show(x.Text + " \nReceived by: ColleagueC");
10: });
11: }
This method is supported in WPF only since in Silverlight you cannot call an anonymous method (via reflection). If you try to use this method in Silverlight you will get an InvalidOperationException saying “This method is not supported in Silverlight”.
Conclusion
I packaged the Mediator in a class library so that you can go ahead and add a reference and reuse this library in your day to day projects.As always feedback is much appreciated.





[...] Mediator v2 for MVVM WPF and Silverlight applications (Marlon Grech) [...]
Pingback by Dew Drop - April 16, 2009 | Alvin Ashcraft's Morning Dew | April 16, 2009 |
Thanks for putting in all the extra effort to make this a reality, man! You rock.
Team Josh + Marlon rock
[...] Mediator v2 for MVVM WPF and Silverlight applications http://marlongrech.wordpress.com/2009/04/16/mediator-v2-for-mvvm-wpf-and-silverlight-applications/ [...]
Pingback by kevin Mocha - Model-View-ViewModel | May 8, 2009 |
The zipped archive seems to be broken. Can you please check?
Thanks
Nevermind, I did a “save target as”, instead of directly clicking on it…which takes you to another page
Flooding the comments with yet another entry: do you have a good example of showing a child window using the MVVM and Mediator patterns? The best I could think of so far is using overlays and toggling visibility by setting the value of a boolean property on the view model (e.g. ChildWindowVisible), and the viewmodels themselves “communicating” data (be it initial data for the child window or the child window’s “result”) using the Mediator.
Is there a cleaner/nicer way to do it? Maybe not using overlays but creating the child window on demand and then letting go of it? (again, here the best I could think of is that the model raises an event to which the view subscribes, something like ShowChildWindowRequested, the view creates and shows the child window; the “result” data is still passed only between view models using the mediator, but the initial data for the child window is a problem for me, as I can’t figure out a clean way to pass it)
This is great, thanks !
Is there a way to pass 0 parameter without defining the type “SomeData” ?
Something like this :
Mediator.NotifyColleagues(MediatorMessages.Message1);
I tried to edit the NotifyColleagues method like this :
public void NotifyColleagues(string message)
But it’s not working
Thanks for your help
you can always pass null….
This is a good mediator implementation but it doesn’t seem to actually solve the problem. It seems that the ViewModel in this approach is responsible for displaying the MessageBox. What happens when the view is something that doesn’t support MessageBoxes? Technically the ViewModel should not know about the View at all.
It seems that a more correct solution would be one that puts the ViewModel into a state that indicates a message should be displayed. The View would then handle that a change to the “display message state” and display a message. The ViewModel would of course need to expose one or more Commands for the View to execute after the message has been processed. What do you guys think?
BTW you should look at open instance (AKA Unbound delegates) delegates as they could make the WeakAction class more efficient.
RE: Message box in ViewModel
This was just to show how you can register methods to be called when a specific message is broadcasted. To see the Mediator in action have a look at
http://marlongrech.wordpress.com/2009/04/08/mvvm-mediator-acb-cool-wpf-app-the-mvvm/
What you are saying is correct. The View is totally responsable of displaying the message…
Very great job ! Thanks a lot !
Hi Marlon,
Thanks very much for your post – I’ve used Mediator in three projects already! Today was actually the first time I tried to call Mediator.Register(myObject) on an object of a class that declaritively registered a parameterless Action delegate. I was getting an InvalidOperationException – the runtime registration was checking if my parameter count was exactly equal to 1. I think it should be the other way around -> if > 1 throw an exception (to account for parameterless calls). The following fix seems to work for me (and matches the Messenger class on Codeplex):
if (methodInfo.GetParameters().Length > 1)
throw new InvalidOperationException(“The registered method should only have 1 parameter since the Mediator has only 1 argument to pass”);
else
invocationList.AddAction(attribute.Message, target, methodInfo, attribute.ParameterType);
Yours,
David
David,
I have tried to implement a parameterless NotifyColleages, however I cannot seem to get it to work. I’ve added the following method:
public void NotifyColleagues(string message)
{
var actions = invocationList.GetActions(message);
if (actions != null)
actions.ForEach(action => action.DynamicInvoke());
}
However, this doesn’t seem to work. In the register method it appears that Josh added logic to check for null parameters already. Am I missing something?
Oh, and I forgot to add – I removed the generic signature of the parameterless version of NotifyColleagues:
//public void NotifyColleagues(string message)
public void NotifyColleagues(string message)
Hi,
First, I would like to thank you for all the work you’ve done, your blog is very clear.
But When I downloaded your packaged library and added it to my Silverlight project, I encountered a compatibility error (it says the library is not packaged for Silverlight).
I’ve not been able to find the mediator library for Silverlight accross the web. Do you know where I can find it?
Best regards,
Elverion
That’s because the compilation is not for SL you can just create a Silverlight assembly and copy the .cs files there
[...] >> Well for starters I am using a new and improved version of the Mediator. The new version of the Mediator API is much more user friendly. It support Generics and it also eliminates the need to have interfaces implemented for you ViewModel. Instead of implementing an interface and have a switch for the message type now you simple register a message to an Action delegate. The Action delegate can be of a generic type so that you don’t even have to cast the argument. You can read more on this over here. [...]
Pingback by Article revisited: MVVM + M (implementing a Fly Weight TreeView) « C# Disciples | September 14, 2009 |
[...] of value to solve this challenge. Another piece of the puzzle is the EventAggregator (Prism term) / Mediator / Message Bus / Messenger that provides the communication mechanism between your views and view [...]
Pingback by MVVM Mediator Deluxe - Yves Reynhout's Blog | October 6, 2009 |
First, I would like to tank you for all the work wou’ve done. It have helped me many times.
This implementation seems greet; but I have a problem with it: it cannot register static functions.
It’s a pity, because when you write a lambda or anonymous method which don’t make use of instance field, the C# compiler compile it as a static method.
I often use this in unit tests (for example, in a lambda method wich set a boolean).
Do you see a way for registering static methods.
Thank’s
And, furthermore, insn’t it dangerous that resitering and notifying parameters cannot be verified at design time by the compiler ?
Examples :
- in ColleagueB, the following code crashes at run time :
Mediator.NotifyColleagues(MediatorMessages.Message1, “stubStringParameter”);
- in ColleagueC, the following code crashes at run time :
Mediator.Register(MediatorMessages.Message1,
(Action) delegate
{
Test = “Received message ‘MediatorMessages.Message1′.”;
MessageBox.Show(Test + ” \nReceived by: ColleagueC”);
});
Is there a way to obtain a compile time verification ?
cheers.
[...] the first problem of triggering the property changed events, I’m using the Mediator v2 solution which allows the ThreadMetrics instance to advise the ThreadMetricsViewModel of changes [...]
Pingback by Binding updates from outside the ViewModel to the View « Christopher Pope's Blog | November 22, 2009 |