Posted by: Zeeshan Amjad | January 11, 2012

Using Mediator to communicate between user controls: Part 5


We made 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 parameter to the call back method and then do the casting. In other words it is not type safe. The other problem is that once message is registered then there is no way to unregister the message. Let’s handle both problems one by one.

Type safety we can handle by using generics. But there is going to be one change in the internal data structure i.e. dictionary to store call back method. Now we don’t know in advance what will be the parameter of the call back method, therefore we are creating list of Delegate instead of Action<object>. Here is our new dictionary object to store call back methods against messages.

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

 

Now come to Register method. We make this method generic so we can pass any parameter to Action delegate. Here is modified code of our Register method.

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

 

Note that we can add the Action<T> type object inside the List<Delegate>. Now we have to specify the parameter type of the delegate at the time of registering the message. Here is a code to register the callback methods.

Code Snippet
Mediator = new Mediator();
Mediator.Register<Student>("mymessage1", studentform.Notify);
Mediator.Register<Student>("mymessage1", studentlist.Notify);
    
Mediator.Register<Student>("mymessage2", studentform.AnotherNotify);

 

Its main advantage is that now we don’t have to do the typecasting inside the call back method, because call back method accept the correct type. Our call back method is quite simple now. Here is a code of our new call back method.

Code Snippet
public void Notify(Student student)
{
    DataContext = student;
}

 

Now let’s come to another problem i.e. unregister the callback method. This code is almost opposite of register method. First check if there is any method register against the given method, then remove it and if the list of call back methods empty then remove that entry from the registry. Here is a code of unregister method.

Code Snippet
public void UnRegister<T>(string message, Action<T> action)
{
    if (actions.ContainsKey(message))
    {
        List<Delegate> actionlist = actions[message];

        actionlist.Remove(action);

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

 

Here is the usage of this method.

 

Code Snippet
Mediator.UnRegister<Student>("mymessage1", studentform.Notify);
Mediator.UnRegister<Student>("mymessage1", studentlist.Notify);
Mediator.UnRegister<Student>("mymessage1", studentlist.Notify);

 

Note that even if we register the same call back method more than once, then it has no effect. Here is complete C# code of our new mediator class.

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

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

        public Mediator()
        {
        }

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

        public void UnRegister<T>(string message, Action<T> action)
        {
            if (actions.ContainsKey(message))
            {
                List<Delegate> actionlist = actions[message];

                actionlist.Remove(action);

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

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

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

 

Here is a complete code of our main program.

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

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

            DataContext = students;
        }
    }
}

 

Code for both user controls are almost same the only difference is now our call back methods accept the parameter of given type not the object, so we don’t have to down cast it.

Here is a 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, Student student)
        {
            Mediator.Send<Student>(message, student);
        }

        public void Notify(Student message)
        {

        }
    }
}

 

And here is C# code of 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(Student student)
        {
            DataContext = student;
        }
    }
}

The output of the program is same as previous programs.

mediator_02_output

Advertisements

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: