Posted by: Zeeshan Amjad | January 9, 2012

Using Mediator to communicate between user controls: Part 4


We made a good progress in last part of the mediator article by removing the dependencies of any interface by registering the call back method and execute it by calling DynamicInvoke method here. But there is still one problem in it that it will call all the register call back method whenever we send a message from any class. There is no way to send message to specific client.

Now lets enhance a mediator class a bit and introduced a concept of message type. Now we can define the message type and register the call back method for that particular message. By doing this we have two advantages. First now we can define as may messages as we want, second we can specify the target of for specific message rather than broadcast that message to everywhere.

We created Dictionary for messages and call back methods.

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

 

We modified our register method little bit and now it accept the message type and the call back method. For simplicity we select message of string type, but we can use alternative approaches too. Our Register method simply check if there isn’t any entry for that message in the dictionary then create an empty list object, or if there is already one entry then simply add the new method to existing list. Here is our Register method.

Code Snippet
public void Register(string message, Action<object> action)
{
    if (!actions.ContainsKey(message))
    {
        actions[message] = new List<Action<object>>();
    }

    actions[message].Add(action);
}

 

Similarly at the time of sending the messages we first check if there is any call back method register for that particular message and if there is then call all of them one by one. Here is a code of Send message of mediator class.

Code Snippet
public void Send(string message, object param)
{
    if (actions.ContainsKey(message))
    {
        List<Action<object>> actionslist = actions[message];

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

 

Here is complete code of our modified Mediator class.

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

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

        public Mediator()
        {
        }

        public void Register(string message, Action<object> action)
        {
            if (!actions.ContainsKey(message))
            {
                actions[message] = new List<Action<object>>();
            }

            actions[message].Add(action);
        }

        public void Send(string message, object param)
        {
            if (actions.ContainsKey(message))
            {
                List<Action<object>> actionslist = actions[message];

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

 

Now the main difference is we have to register the call back method with the message type. Here is a main view code to register the call back functions.

Code Snippet
Mediator = new Mediator();
Mediator.Register("mymessage1", studentform.Notify);
Mediator.Register("mymessage1", studentlist.Notify);

Mediator.Register("mymessage2", studentform.AnotherNotify);

studentform.Mediator = Mediator;
studentlist.Mediator = Mediator;

 

Note that we register two different type of messages “mymessage1” and “mymessage2”. There is only one method register for “mymessage2”, but two methods register for “mymessage1”.

Here is a complete C# code of main view class.

Code Snippet
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" });

            Mediator = new Mediator();
            Mediator.Register("mymessage1", studentform.Notify);
            Mediator.Register("mymessage1", studentlist.Notify);

            Mediator.Register("mymessage2", studentform.AnotherNotify);

            studentform.Mediator = Mediator;
            studentlist.Mediator = Mediator;

            DataContext = students;
        }
    }
}

 

Just for demonstration purpose we send both “mymessage1” and “mymessage2” messages whenever there is selection change in the CustomizedList user control.

Code Snippet
private void list_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    SelectedStudent = list.SelectedItem as Student;
    Send("mymessage1", SelectedStudent);
    Send("mymessage2", SelectedStudent);
}

 

First Send method call all the methods register for “mymessage1” (studentform.Nofity and studentlist.Nofity) and second Send method call all the methods register for “mymessage2” (studentform.AnotherNofity).

Here is a complete code of our StudentForm user control.

Code Snippet
using System.Windows.Controls;

namespace MediatorUserControl
{
    /// <summary>
    /// Interaction logic for StudentForm.xaml
    /// </summary>
    public partial class StudentForm : UserControl
    {
        public Mediator Mediator
        { get; set; }

        public StudentForm()
        {
            InitializeComponent();
        }

        public void Notify(object message)
        {
            Student student = message as Student;

            if (student != null)
            {
                DataContext = student;
            }
        }

        public void AnotherNotify(object message)
        {

        }
    }
}

 

And here is complete C# code of CustomizedList user control.

Code Snippet
using System.Windows;
using System.Windows.Controls;

namespace MediatorUserControl
{
    /// <summary>
    /// Interaction logic for CustomizedList.xaml
    /// </summary>
    public partial class CustomizedList : UserControl
    {
        public static readonly DependencyProperty SelectedStudentProperty =
            DependencyProperty.Register("SelectedStudent", typeof(Student), typeof(CustomizedList));

        public Student SelectedStudent
        {
            get { return (Student)GetValue(SelectedStudentProperty); }
            set { SetValue(SelectedStudentProperty, value); }
        }

        public Mediator Mediator
        { get; set;  }

        public CustomizedList()
        {
            InitializeComponent();
        }

        private void list_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            SelectedStudent = list.SelectedItem as Student;
            Send("mymessage1", SelectedStudent);
            Send("mymessage2", SelectedStudent);
        }

        public void Send(string message, object param)
        {
            Mediator.Send(message, param);
        }

        public void Notify(object message)
        {

        }
    }
}

 

The output of this program is same as previous program.

mediator_02_output

Advertisements

Responses

  1. […] a mediator that can register different type of messages with different type of call back methods here. But there are still two problems in it. The first problem is that we have to pass the object as a […]

  2. Am I able to use this for web application? I want to use MVC pattern combined with the Mediator pattern to make my controllers loosly coupled.

    But my application is going be large. There will be 20.000 users. We expect 1000 concurrent users.

    I have created controllers. Each controller have his own view. (Like: PersonListController and PersonListView). With the mediator the send messages to each other without knowing of their existing. The problem is, when defining the controllers at this niveau then there will be over 1000 controllers.

    Thank you,
    Ahmet

    • Thanks for reading my blog. Technically there is nothing stopping you to use the mediator in ASP.Net application using MVC. But this mediator is not complete yet. Currently this is having a strong reference of all the register classes, which is good if we know when to unregister the class. But sometimes we don’t know the object lifetime and in that case keeping a strong reference prevent garbage collector to collect the memory of that reference. Ideally this mediator should have the WeakReference insteaed of Strong references.

      Regards
      Zeeshan Amjad


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: