Posted by: Zeeshan Amjad | March 5, 2012

Using Mediator to communicate between user controls: Part 7


Till now our mediator class does reasonable work, send message to appropriate handler in multi threaded environment. But there is still one major problem in it. We have a strong reference of our receiver in our mediator class. It means that we have to unregister it ourselves. If the receiver dies without unregistering it, then garbage collector doesn’t collect it from the memory, because it has a strong reference in the mediator class. To overcome this problem we are suppose to use GCHandleType.Weak when allocating reference in mediator class so garbage collector can collect it. Fortunately we already have a wrapper for this named WeakReference class. So we are going to use the WeakReference class to store the receiver.  But first we have to change the signature our our Register and UnRegister method to not only accept the message, delegate, but also the receiver (it is also known as target). Here is our updated signature.

Code Snippet
public void Register<T>(string message, object receiever, Action<T> action)

 

Our first attempt would be to use List of WeakReference, “List<WeakReference> as a value in our actions dictionary. But we can store only the receiver (or target) in the WeakReference constructor, not the delegate. Therefore we are going to make a wrapper on top of WeakReference to store not only receiver (target) but also delegate. Here is our wrapper on top of WeakReference.

Code Snippet
namespace MediatorUserControl
{
    using System;

    public class WeakReferenceWrapper
    {
        private WeakReference weakReference;

        public WeakReferenceWrapper(object receiever, Delegate action)
        {
            weakReference = new WeakReference(receiever);
            this.Action = action;
        }

        public Delegate Action
        { get; set; }
    }
}

 

Now we are going to use this class in the actions dictionary as a values.

Code Snippet
private Dictionary<string, List<WeakReferenceWrapper>> actions =
    new Dictionary<string, List<WeakReferenceWrapper>>();

 

Here is updated version of our mediator class.

Code Snippet
using System;
using System.Collections.Generic;

namespace MediatorUserControl
{
    public class Mediator
    {
        private Dictionary<string, List<WeakReferenceWrapper>> actions =
            new Dictionary<string, List<WeakReferenceWrapper>>();

        public Mediator()
        {
        }

        public void Register<T>(string message, object receiever, Action<T> action)
        {
            if (message == null)
            {
                throw new ArgumentNullException("message");
            }

            if (action == null)
            {
                throw new ArgumentNullException("action");
            }

            lock (actions)
            {
                if (!actions.ContainsKey(message))
                {
                    actions[message] = new List<WeakReferenceWrapper>();
                }

                actions[message].Add(new WeakReferenceWrapper(receiever, action));
            }
        }

        public void UnRegister<T>(string message, object receiever, Action<T> action)
        {
            if (message == null)
            {
                throw new ArgumentNullException("message");
            }

            if (action == null)
            {
                throw new ArgumentNullException("action");
            }

            lock (actions)
            {
                if (actions.ContainsKey(message))
                {
                    List<WeakReferenceWrapper> actionlist = actions[message];

                    actionlist.Remove(new WeakReferenceWrapper(receiever, action));

                    if (actionlist.Count == 0)
                    {
                        actions.Remove(message);
                    }
                }
            }
        }

        public void Send<T>(string message, T param)
        {
            if (message == null)
            {
                throw new ArgumentNullException("message");
            }

            lock (actions)
            {
                if (actions.ContainsKey(message))
                {
                    List<WeakReferenceWrapper> actionslist = actions[message];

                    foreach (var action in actionslist)
                    {
                        action.Action.DynamicInvoke(param);
                    }
                }
            }
        }
    }
}

 

Of course we have to change the client application where we are calling the Register or UnRegister method. Here is updated version of our client.

Code Snippet
using System;
using System.Collections.ObjectModel;
using System.Windows;

namespace MediatorUserControl
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        private ObservableCollection<Student> students = new ObservableCollection<Student>();

        public Mediator Mediator
        { get; set; }

        public MainWindow()
        {
            InitializeComponent();

            students.Add(new Student() { ID = "1", FirstName = "Bob", LastName = "Smith", Degree = "Associate" });
            students.Add(new Student() { ID = "2", FirstName = "Alex", LastName = "White", Degree = "Certificate" });
            students.Add(new Student() { ID = "3", FirstName = "Chris", LastName = "Martin", Degree = "Master" });
            students.Add(new Student() { ID = "4", FirstName = "Michal", LastName = "Brown", Degree = "Bachlor" });

            try
            {
                Mediator = new Mediator();

                Mediator.Register<Student>("mymessage1", studentform, studentform.Notify);
                Mediator.Register<Student>("mymessage1", studentlist, studentlist.Notify);

                studentform.Mediator = Mediator;
                studentlist.Mediator = Mediator;
            }
            catch (Exception e)
            {
                MessageBox.Show(e.Message);
            }

            DataContext = students;
        }
    }
}

 

The output of this program is same as previous program.

mediator_02_output

Advertisements

Responses

  1. […] using C++ We already saw a series of example to implement Mediator design pattern in C# here. We can do the same thing with the help of STL, function pointer and template in C++ in unmanaged […]

  2. […] Here is a mediator class defined here. […]

  3. […] mediator pattern we created here works quite well for most of my scenario where i am suppose to use int message not string messages. […]

  4. This will to work, registered objects will never going to be GCed because theirs references are held in Action delegates

  5. Thank you for your detailed description :). Appreciate your all your efforts on this series.

    • Thanks to like it. I am glad that it helped.


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

Categories

%d bloggers like this: