65.9K
CodeProject is changing. Read more.
Home

Loosely Coupled Delegated Events Between Parent and UserControls

starIconstarIconstarIconstarIconstarIcon

5.00/5 (1 vote)

Jul 11, 2012

CPOL

2 min read

viewsIcon

21795

downloadIcon

350

Loosely coupled communication between usercontrols and parent.

Introduction

I want to demonstrate how you can design loosely coupled events between user-controls and their parent or between user-controls themselves.

Background

A good design pattern should cater for loosely coupled controls within an application, a parent shouldn't need to know about what user controls it has within the project, only that the parent has the delegated events awaiting calls from child controls to act upon them - be the user control added to the application or not.

Project structure  

Below is the structure of the Visual Studio 2010 application: 

Fig 1

In the project I have a MainWindow class that will host the two user control objects. There is an argument class (carArgs) that will be used to communicate data between the user controls and the parent and vice versa. There is also an XML file just to bind some data to the grids.

The idea of the application is to have two controls within the parent. The parent has a grid (so do the user controls). When I click on a row within the parent grid and select which control I wish to send the data to (which will select that row too, in that user control), and vice versa, the control can make the parent control select the row that is in the child control. The parent doesn't need to know anything about the child, only have the delegated event available to pick-up the call from the child and act upon it.  

The code explained

Main Class (XAML)

The important piece of code when adding the user control is the event binding. In the code below, the user control has a public event called NotifyedUserControl that the parent binds to its method called ctrl_NotifyedUserControl so when the child user control calls the method event within its own class, this will find its way back to the parent method ctrl_NotifyedUserControl along with any parameters that were declared within the signatures. 

<UserControls:HrCars Grid.Row="0" Grid.Column="0" 
   x:Name="ctrlLoadHr" Margin="5" 
   IsEnabled="true" VerticalAlignment="Center" 
   HorizontalAlignment="Center" NotifyedUserControl="ctrl_NotifyedUserControl"></UserControls:HrCars>

The Main class (C#) 

Below you can see the private method ctrl_NotifyedUserControl that is bound to the child control NotifyedUserControl event. It will then call the public methods with the other control or a method within the main class to select the appropriate row within the grid. 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using UserControlEventing.Models;

namespace UserControlEventing
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        #region Class Variable
        public CarArgs ParamEventArg { get; set; }
        #endregion

        public MainWindow()
        {
            InitializeComponent();            
        }

        #region Delegate Event Binding
        private void ctrl_NotifyedUserControl(object sender, Models.CarArgs arg)
        {
            switch (arg.ControlDestination.ToString())
            {
                case "Hr":
                    this.ctrlLoadHr.SetUserControl(arg);
                    break;
                case "Sales":
                    this.ctrlLoadSales.SetUserControl(arg);
                    break;
                case "Main":
                     this.mainDataGrid.SelectedIndex= arg.SelectedDatagridRowIndex;                                                     
                    break;
            }
        }
        #endregion

        #region Button Event
        private void btnCallControls_Click(object sender, RoutedEventArgs e)
        {            
            ParamEventArg = new CarArgs();
            ParamEventArg.SelectedDatagridRowIndex = mainDataGrid.SelectedIndex;
            ParamEventArg.ControlDestination = (ControlType)Enum.Parse(typeof(ControlType), 
              ((ComboBoxItem)this.cboUserControls.SelectedItem).Tag.ToString(), true);

            switch (this.cboUserControls.SelectionBoxItem.ToString())
            {
                case "Hr UserControl":
                    this.ctrlLoadHr.SetUserControl(ParamEventArg);
                    break;
                case "Sales UserControl":
                    this.ctrlLoadSales.SetUserControl(ParamEventArg);
                    break;
                case "Both UserControls":
                    this.ctrlLoadHr.SetUserControl(ParamEventArg);
                    this.ctrlLoadSales.SetUserControl(ParamEventArg);
                    break;
            }
        }
        #endregion
    }
}

Control code (C#)

Notice the declaration of the delegate - NotifyUserControl, its signature and the event - NotifyedUserControl. The carArgs class is also passed back to the parent or another user control. 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using UserControlEventing.Models;

namespace UserControlEventing.UserControls
{
    /// <summary>
    /// Interaction logic for HrCars.xaml
    /// </summary>
    public partial class HrCars : UserControl
    {
        public delegate void NotifyUserControl(object sender, CarArgs arg);
        public event NotifyUserControl NotifyedUserControl;

        public CarArgs ParamEventArg { get; set; }

        public HrCars()
        {
            InitializeComponent();
        }

        public void SetUserControl(CarArgs arg)
        {
            this.dataGrid.SelectedIndex = arg.SelectedDatagridRowIndex;
        }

        private void btnCallControls_Click(object sender, RoutedEventArgs e)
        {            
            ParamEventArg = new CarArgs();
            ParamEventArg.SelectedDatagridRowIndex = this.dataGrid.SelectedIndex;
            ParamEventArg.ControlDestination = (ControlType)Enum.Parse(typeof(ControlType), 
              ((ComboBoxItem)this.cboUserControls.SelectedItem).Tag.ToString(), true);

            this.NotifyedUserControl(this, ParamEventArg);            
        }
    }
}

Argument class (C#)

namespace UserControlEventing.Models
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Windows.Controls;

    /// <summary>
    /// TODO: Update summary.
    /// </summary>
    public class CarArgs
    {
        public int SelectedDatagridRowIndex { get; set; }
        public ControlType ControlDestination { get; set; }
    }

    public enum ControlType
    {
        Hr,
        Sales,
        Main,
        Hr_Sales
    }
}

The application running 

Below you can see the application running, with the two user controls and the parent. If you select a row in the parent and select a user control from the dropdown and click Push to control, you can see that the other control selects the same row as the calling event (fig 3). 

Fig 2

Fig 3