Posted by: Zeeshan Amjad | January 5, 2012

Using Mediator to communicate between user controls: Part 1


We saw one example of user control with dependency property here. Now we are going to introduce one more  user control and see how they communicate with each other using mediator design pattern. This is a first and naïve approach of mediator pattern that I am planning to improve in future.

Let’s first take a look at user control. Our first user control is a customized list. This control is very much similar to the control defined in this post. The only different is it display the student information in the list box and display the selected student first name at the top. Here is our student class.

Code Snippet
public class Student
{
    public string ID
    { get; set; }

    public string FirstName
    { get; set; }

    public string LastName
    { get; set; }

    public string Degree
    { get; set; }
}

 

Here is a XAML of our customized list box user control.

Code Snippet
<UserControl x:Class="MediatorUserControl.CustomizedList"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml&quot;
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006&quot;
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008&quot;
             mc:Ignorable="d"
             d:DesignHeight="300" d:DesignWidth="300">
    <DockPanel>
        <Border DockPanel.Dock="Top" Margin="5"
                BorderThickness="2" BorderBrush="Black" CornerRadius="5">
            <TextBlock Margin="3" Foreground="Blue"
                       Text="{Binding ElementName=list, Path=SelectedItem.FirstName}">
            </TextBlock>
        </Border>
        <ListBox Margin="5" ItemsSource="{Binding}" x:Name="list"
                 HorizontalContentAlignment="Stretch" SelectionChanged="list_SelectionChanged">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <StackPanel Margin="3" >
                        <TextBlock Text="{Binding ID}"/>
                        <TextBlock Text="{Binding FirstName}"/>
                        <TextBlock Text="{Binding LastName}"/>
                        <TextBlock Text="{Binding Degree}"/>
                    </StackPanel>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
    </DockPanel>
</UserControl>

 

Note we handle the SelectionChange event here to update the dependency property of currently selected student. Here is C# code of our 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 CustomizedList()
        {
            InitializeComponent();
        }

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

In a same way we created a user control to display the record of individual student. Here is XAML code for our Student form user control.

Code Snippet
<UserControl x:Class="MediatorUserControl.StudentForm"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml&quot;
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006&quot;
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008&quot;
             mc:Ignorable="d"
             d:DesignHeight="300" d:DesignWidth="300">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition/>
            <RowDefinition/>
            <RowDefinition/>
        </Grid.RowDefinitions>
        <TextBlock Grid.Column="0" Grid.Row="0" VerticalAlignment="Center" Margin="5" Text="ID"/>
        <TextBox Grid.Column="1" Grid.Row="0" VerticalAlignment="Center" Margin="5" Text="{Binding ID}"/>
        <TextBlock Grid.Column="0" Grid.Row="1" VerticalAlignment="Center" Margin="5" Text="FirstName"/>
        <TextBox Grid.Column="1" Grid.Row="1" VerticalAlignment="Center" Margin="5" Text="{Binding FirstName}"/>
        <TextBlock Grid.Column="0" Grid.Row="2" VerticalAlignment="Center" Margin="5" Text="LastName"/>
        <TextBox Grid.Column="1" Grid.Row="2" VerticalAlignment="Center" Margin="5" Text="{Binding LastName}"/>
        <TextBlock Grid.Column="0" Grid.Row="3" VerticalAlignment="Center" Margin="5" Text="Degree"/>
            <TextBox Grid.Column="1" Grid.Row="3" VerticalAlignment="Center" Margin="5" Text="{Binding Degree}"/>
    </Grid>
</UserControl>

 

There is nothing special in the C# code of this user control. We put both of our control in the main window. Here is a XAML code of our main window.

Code Snippet
<Window x:Class="MediatorUserControl.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml&quot;
        xmlns:local="clr-namespace:MediatorUserControl"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        <local:CustomizedList x:Name="studentlist" Grid.Column="0" Margin="5"/>
        <local:StudentForm x:Name="studentform" Grid.Column="1" Margin="5"/>
    </Grid>
</Window>

 

And we simply create an object of ObservableCollection class of Student type and assign it to the DataContext property of the main window. Now if we run the application then it display all the student in the listbox and display the detail of first student in the collection in the student form user control.

Now even if I select any other student in the student list user control that changes in not going to reflect in student form user control, because there is no communication between these two controls.

One solution is to introduce one dependency property in student form user control too and then do the user control to user control binding using ElementName property of the binding class. In this simple example that may be a good solution. But what will the situation if there are lots of  user control and they are trying to communicate with each other?

Mediator design patter is a good solution here. Mediator design pattern introduced a middle layer and every component is suppose to communicate with it. It is just like a control tower of the airport. During the landing or take off all the airplane are suppose to communicate with control tower, not with each other.

Now let’s make a very basic implementation of mediator. In our example we are concern with two user control so for simplicity we uses the references of both user control in our mediator class. The purpose of mediator in this case is very simple. Whenever get the Send message from anywhere, then notify both user controls.

Here is a simple implementation of Mediator class.

Code Snippet
namespace MediatorUserControl
{
    public class Mediator
    {
        private StudentForm form;
        private CustomizedList list;

        public Mediator(StudentForm form, CustomizedList list)
        {
            this.form = form;
            this.list = list;
        }

        public void Send(object message)
        {
            form.Notify(message);
            list.Notify(message);
        }
    }
}

 

Our next step is to introduce the mediator property in both user control. Now our mediator has a reference of both user controls and user control has reference of mediator class. In this way user control communicate with each other user mediator.

Here is a code of our main class to demonstrate this.

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(studentform, studentlist);
            studentform.Mediator = Mediator;
            studentlist.Mediator = Mediator;

            DataContext = students;
        }
    }
}

 

This relationship can be shown by this simple diagram.

 

Mediator_01

 

From the CustomizedList user control we are sending the message to the mediator. Here we send the selected student to the mediator. Here is a code for this.

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

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

 

On the other hand we handle the message in StudentForm user control. Here is a code to handle the message.

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

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

 

Now we set the data context of the user control to the selected student.

Here is complete code of customized list 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(SelectedStudent);
        }

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

        public void Notify(object message)
        {

        }
    }
}

 

Here is complete C# code of the student form 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;
            }
        }
    }
}

 

Here is the output of the program

Mediator_01_Output

Advertisements

Responses

  1. Hey, your solution is quite fine but I was wondering how do you implement the mediator in a “bigger project” ? Actually I mean that a mediator is directly related to a context and you need to create one mediator for each scenarios ?

    for instance, I have a Master Detail View for students, so a UserControl for the student list and another UserControl for the selected student detail. I will need a mediator for managing it. And if I have a Master Detail View for Books, so a UserControl for the list and another for the detail, will I need to create another mediator or will I use the same mediator as for the students ?

    Thank you for answer, your articles are fine 🙂

  2. […] between user controls: Part 2 We saw our first cut and very naïve example of mediator here. Although it works in that example but there are several limitations in it. The biggest problem is […]


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: