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.

Download MediatorLib + Sample application

About these ads

44 thoughts on “Mediator v2 for MVVM WPF and Silverlight applications

  1. Pingback: Dew Drop - April 16, 2009 | Alvin Ashcraft's Morning Dew

  2. Pingback: kevin Mocha - Model-View-ViewModel

  3. Nevermind, I did a “save target as”, instead of directly clicking on it…which takes you to another page ;)

  4. 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) :(

    • Hi Adrian!

      Don’t know if this will help you. In our solution we defined a WorkspaceManager that manages and controls all open Workspaces (=ChildWindows). The WorkspaceManager has a Property called CurrentWorkspaceViewModel that is bound to the Content of a ContentPresenter. The View of the WorkpsaceViewModel is controlled via a Typed DataTemplate. The WorkspaceManager listens to a ShowWorkspaceMessage broadcasted by the Mediator.

      Best regards
      Domi

  5. 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 :)

  6. 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.

  7. 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?

  8. 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)

  9. 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

  10. Pingback: Article revisited: MVVM + M (implementing a Fly Weight TreeView) « C# Disciples

  11. Pingback: MVVM Mediator Deluxe - Yves Reynhout's Blog

  12. 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

  13. 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.

  14. Pingback: Binding updates from outside the ViewModel to the View « Christopher Pope's Blog

  15. Hello,
    I am currently using page based navigation in WPF. We have controls in the Window that need to listen to events coming from different elements on the page. We are currently using MVVM, but we are not tied to it.

    I’m a little worried about using this class due to the use of WeakReferences. I’m not savvy at all about it (I’ve read an MSDN article), and I was hoping someone could briefly describe how a weakreferences becomes a strong reference in context of the demo app provided with this framework? Thanks!

  16. I’ve implemented this Meidator pattern in an MVVM WPF application, and it seemed to be working until recently. What we are doing is dynamically opening modal windows via a broker type object called from the ViewModel. Each modal that opens has a ViewModel intance assigned as it’s datacontext obviously. When the instance of the ViewModel is created it will register with the mediator any delegates used for receiving messages. When the Window is closed the datacontext (the viewmodel) is set to null thus I would think destroys any strong reference to that viewmodel.

    The problem seems to be that the Mediator is maintaining a reference to that viewmodel even though it has been wrapped in a weak reference. So when you open an new instance of the modal window and viewmodel (which again registers with the mediator) there are multiple references to viewmodels of the same type, but not the same instance. The side effects are that when messages are called, they act on each instance of that viewmodel producing multiple unintended results, and leads to considerable memory leak.

    Am I missing something, because it does not seem that the weakreferences are being collected, and I can’t seem to determine any strong reference to the viewmodel instances. The only reference that I see is that of the weakreference. Any insight would be greatly appreciated. Thanks.

  17. The viewmodels haven’t been garbage collected yet – you can force that by calling gc.collect when you open the instance of your modal window.

    • Sorry Nick thats not the problem or a fix. The weak references are still alive and not being released properly. The work around i had to do was change some code in the map and then manually unsubscribe all my VMs when they were disposed of

      • Justin-

        I did exactly the same. Works like a charm…well works. feels like a hack, but works.

        I have a base ViewModel class that implements IDispose that is inherited by all ViewModels. So when the “broker” closes any window, it inspects the datacontext to see if it implements IDispose (which all of my VMs do via inheritence) and calls dispose() where, like you said, unregisters itself with the mediator.

        I still use somewhat the same pattern as this example, but I have no need for a weak reference now since I have to manually unregister (null the target). I’m looking into Prism now, and hope that has a better means for communication between VMs.

        Cheers
        Jim

    • Nick –

      I tried that. It doesn’t work, plus calling gc directly is not a good practice from what I understand. The reason it is not being collected seems to be that the IsActive remains true with target pointing to the VM reference, thus gc sees this and passes it up as an object with a live reference. See below with my workaround. It seems similar to Justin’s.

      Happy Coding
      Jim

      • Jim,

        Nice work. Thats exactly what I did. Shame we have to do this. The mediator is supposed to release the references for me, so that I as a developer dont have to litter my code with all these unsubscribes. Oh well the mediator is cool with the attributes and registration….im just really bummed about the weak references not working.

        Good luck man!

  18. Pingback: WPF and Role Based Security Framework – Part 2 | Brad W. Young's Blog

  19. Phanx for taking my code in advance.

    It would be very clever for you to use my version of mediator with your declarattiva approach. Als0, there is an error of your vision of mediator, because you even didn’t cautch it that my version of mediator works slightly divverent as you thought. But, i liked your declarativa approach an will use your attributed version in exaclty the same way,

    Cheers,
    hack2root

  20. will it be actually so simple?
    there’s no doubt that for someone qualified that is possible
    whereas I not really know that everybody is going to think that way

  21. Pingback: A Journey to MVVM « Muhammad Naveed Zahid

  22. A weakness of this solution I noticed in the sample solution MediatorTest is that the classes ColleagueA, ColleagueB, ColleageC all inherit off BaseViewModel. Since c# doesn’t do multiple inheritance I can’t inherit off this class if am already inheriting behaviour off something else. Right now I have a solution where I change the BaseViewModel class to become instead:

    static public class PubSub
    {
    static readonly Mediator mediator = new Mediator();

    static public Mediator Mediator
    {
    get { return mediator; }
    }
    }

    And I then use it by doing

    PubSub.Mediator.Register(this);

    and then

    PubSub.Mediator.NotifyColleagues(MediatorMessages.Message1, new SomeData { Text = “Hello World!” });

    My question is whether I’m going to somehow lose the benefit of having the weak references or is there some other gotcha in doing it this way. Right now am wanting to use this in a windows/wcf service, so having good behaviour is kindof essential.

  23. I know this is an old, old post but I’ve been using this mediator for some projects in WPF. The problem I’m having with the mediator is its weak reference. I have, essentially, a “controller” view model that is the central hub for all mediator notifications and handles the construction of views and their coupling to view models. The problem I’m having is since all these messages are in one “static” view model, some messages may not be called for some time. Of course the gc comes by and clears the reference before they can be restored. For the time being I’m altering the mediator to go back to a strong reference. Future users beware!

  24. Pingback: A Quick Intro to MVVM

  25. Pingback: Fix Wpf Error Template Like Silverlight Windows XP, Vista, 7, 8 [Solved]

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s