Click here to Skip to main content
Click here to Skip to main content

Loosely Coupled Delegated Events Between Parent and UserControls

, 11 Jul 2012
Rate this:
Please Sign up or sign in to vote.
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

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Bert O Neill
Architect
Ireland Ireland
No Biography provided

Comments and Discussions

 
-- There are no messages in this forum --
| Advertise | Privacy | Mobile
Web01 | 2.8.140721.1 | Last Updated 11 Jul 2012
Article Copyright 2012 by Bert O Neill
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid