4.4. Communicating Between User Controls

Problem

You have multiple user controls on a page, and one of the user controls needs to send data to another as, for example, when one control takes its form or content from the user’s action on another.

Solution

Create a source user control, a destination user control, and a web form that contains both user controls. (See Recipe 4.1 and Recipe 4.2 for detailed steps.) In the source user control, create a custom event and raise the event when the required action is performed, such as when a user completes her entry for a form. In the destination user control, create an event handler to receive the event from the source user control. Finally, “wire” the event raised in the source user control to the event handler in the destination user control in the Page_Load event of the web form. The output of a test page showing one user control communicating with another appears in Figure 4-4. The code for our example application that implements the solution is shown in Examples Example 4-15 through Example 4-23. Example 4-15 shows the .ascx file for the source user control. Example 4-16 and Example 4-17 show the VB and C# code-behind for the source user control. Example 4-18 shows the .ascx file for the destination user control. Example 4-19 and Example 4-20 show the VB and C# code-behind for the destination user control. Example 4-21 shows the .aspx file for the web form used to demonstrate the user controls and their interconnection. Example 4-22 and Example 4-23 show the VB and C# code-behind for the demonstration web form.

Communicating between controls output

Figure 4-4. Communicating between controls output

Discussion

Rather than focus on the basic content and creation of user controls, which is the focus of the previous recipes in the chapter, this recipe instead focuses on the interaction between user controls. The approach we advocate for handling this interaction involves creating a custom event for the source user control and raising the event when the communication is to be initiated, such as when the user clicks a button to complete his entry for the form. In order to receive the event from the source user control, the destination user control must have an event handler tailored for that purpose.

In our approach, creating the custom event for the source user control involves creating a custom event argument class, which provides the ability to add a message to the event arguments. It also involves using a delegate, which is a convenient way to pass to the destination user control a reference to an event handler for the OnSend event raised by the source user control.

We’ve created an application to illustrate our approach. Because of the unusually high number of interrelated files, this example may appear a bit overwhelming at first, but it is actually pretty straightforward. Keep in mind that there are three basic pieces:

  • A user control that sends a message (the source)

  • A user control that receives the message (the destination)

  • A web form that contains the two user controls and wires them together

The source user control contains only a button that is used to initiate sending a message.

The source user control code-behind contains the bulk of the code. First, we create a custom event argument class to provide the ability to add the message to the event arguments. This class inherits from System.EventArgs and adds a message property, as shown in Example 4-16 (VB) and Example 4-17 (C#).

Next, we define a new delegate signature, customMessageHandler, to allow the MessageEventArgs object to be passed as the event arguments. Without this delegate, you would have to use the EventArgs object, which does not provide the ability to pass custom information. An event is then defined with this type of delegate.

Tip

A delegate is a class that can hold a reference to a method. A delegate class has a signature, and it can only hold references to methods that match its signature. The delegate object is passed to code that calls the referenced method, without having to know at compile time which method will actually be invoked. The most common example is building a generic sort routine, one that allows you to sort any type of data, where you pass to it not only the data to be sorted but also a reference to the comparison routine needed to compare the particular data. The situation here is somewhat similar. In this case, we are passing a message to the destination user control (contained within an instance of MessageEventArgs) as well as a reference to an event handler for the OnSend event raised by the source user control. A delegate provides the best, most convenient way to accomplish this.

Our remaining task in the source user control code-behind is to provide a standard event handler for the send message button click event. In this handler, an instance of MessageEventArgs is created and populated with the message being sent. The OnSend event is then raised, passing a reference to the source user control as the event source and a reference to the messageArgs object containing the message being sent. In our example, this is a simple hardwired message, but it demonstrates the basic principal.

Warning

In C#, the OnSend event must be checked to make sure it is not null before raising the event. Failure to do so will result in an exception being thrown if no handler is wired to the event. This is not required for VB.

Our example’s destination user control, which is shown in Example 4-18, contains only a label used to display the message sent from the source user control.

The destination user control code-behind, which is shown in VB in Example 4-19 and in C# in Example 4-20, contains a single method to handle the event raised from the source user control. The signature of the method must match the customMessageHandler delegate defined in the source user control. The only operation performed is to update the label in the user control with the message passed in the event arguments.

In our example, the .aspx file for the web form used to demonstrate the user controls, which appears in Example 4-21, registers the two user controls and instantiates each of the controls.

The code-behind for the demonstration web form, which is shown in VB in Example 4-22 and in C# in Example 4-23, provides the glue for tying the event from the source user control to the destination user control. This is done by adding the updateLabel of the destination user control as an event handler for the OnSend event raised by the source user control. What we’re actually doing here is adding a delegate to the source user control’s OnSend event’s event handler list; that list now consists of just one event handler, but can include more.

Tip

Event delegates in .NET are multicast, which allows them to hold references to more than one event handler. This provides the ability for one event to be processed by multiple event handlers. You can try it yourself by adding a label to the demonstration web form, adding a new event handler in the web form, and then adding your new event handler to the OnSend event’s event handler list. This will cause the label on the destination user control and the web form to be updated with the message from the source user control. An example that does this with multiple user controls is shown in Recipe 4.5.

Warning

In VB, when using the event/delegate model, the keyword WithEvents is not used. (Recall that the WithEvents keyword indicates that a declared object variable refers to a class instance that can raise events.) WithEvents and the event/delegate model can be intermixed, but they should not be used for the same event.

See Also

Programming C# or Programming Visual Basic .NET, both by Jesse Liberty (O’Reilly), for more about delegates

Example 4-15. Communicating between controls—source user control (.ascx)

<%@ Control Language="vb" AutoEventWireup="false" 
      Codebehind="CH04UserControlCommSourceVB.ascx.vb" 
      Inherits="ASPNetCookbook.VBExamples.CH04UserControlCommSourceVB" %>
<asp:Button ID="btnSendMessage" runat="server" Text="Send Message" />

Example 4-16. Communicating between controls—source user control code-behind (.vb)

Option Explicit On 
Option Strict On
'-----------------------------------------------------------------------------
'
'   Module Name: CH04UserControlCommSourceVB.ascx.vb
'
'   Description: This module provides the code behind for
'                CH04UserControlCommSourceVB.ascx
'
'*****************************************************************************
Imports System

Namespace ASPNetCookbook.VBExamples
  Public MustInherit Class CH04UserControlCommSourceVB
    Inherits System.Web.UI.UserControl

    'controls on the user control
    Protected WithEvents btnSendMessage As System.Web.UI.WebControls.Button

    'define the delegate handler signature and the event that will be raised
                      'to send the message
                      Public Delegate Sub customMessageHandler(ByVal sender As System.Object, _
                                                               ByVal e As MessageEventArgs)
                      Public Event OnSend As customMessageHandler

    '*************************************************************************
    '
    '   ROUTINE: btnSendMessage_Click
    '
    '   DESCRIPTION: This routine provides the event handler for the send 
    '                message button click event.  It creates a new
    '                MessageEventArgs object then raises an OnSend event
    '-------------------------------------------------------------------------
    Private Sub btnSendMessage_Click(ByVal sender As Object, _
                                                       ByVal e As System.EventArgs) _
                                  Handles btnSendMessage.Click

                        Dim messageArgs As New MessageEventArgs

                        messageArgs.message = "This message came from the source user control"
                        RaiseEvent OnSend(Me, messageArgs)
                      End Sub  'btnSendMessage_Click
  End Class  'CH04UserControlCommSourceVB

  'The following class provides the definition of the custom event arguments
                    'used as the event arguments for the message sent from this control
                    'This class simply inherits from System.EventArgs and adds a message property
                    Public Class MessageEventArgs
                      Inherits EventArgs

                      Private mMessage As String
                      Public Property message( ) As String
                        Get
                          Return (mMessage)
                        End Get
                        Set(ByVal Value As String)
                          mMessage = Value
                        End Set
                      End Property
                    End Class  'MessageEventArgs
End Namespace

Example 4-17. Communicating between controls—source user control code-behind (.cs)

//----------------------------------------------------------------------------
//
//   Module Name: CH04UserControlCommSourceCS.ascx.cs
//
//   Description: This module provides the code behind for
//                CH04UserControlCommSourceCS.ascx
//
//****************************************************************************
using System;

namespace ASPNetCookbook.CSExamples
{
  public abstract class CH04UserControlCommSourceCS : System.Web.UI.UserControl
  {
    // controls on the user control
    protected System.Web.UI.WebControls.Button btnSendMessage;

    // define the delegate handler signature and the event that will be raised
                      // to send the message
                      public delegate void customMessageHandler(System.Object sender,
                                                                MessageEventArgs e);
                      public event customMessageHandler OnSend;

    //************************************************************************
    //
    //   ROUTINE: Page_Load
    //
    //   DESCRIPTION: This routine provides the event handler for the page 
    //                load event.  It is responsible for initializing the 
    //                controls on the user control.
    //------------------------------------------------------------------------
    private void Page_Load(object sender, System.EventArgs e)
    {
      // wire the click event for the send button
      this.btnSendMessage.Click += 
        new System.EventHandler(this.btnSendMessage_Click);
    }  // Page_Load

    //************************************************************************
    //
    //   ROUTINE: btnSendMessage_Click
    //
    //   DESCRIPTION: This routine provides the event handler for the send 
    //                message button click event.  It creates a new
    //                MessageEventArgs object then raises an OnSend event
    //------------------------------------------------------------------------
    private void btnSendMessage_Click(Object sender,
                                                        System.EventArgs e)
                      {
                        MessageEventArgs messageArgs = new MessageEventArgs( );
                        messageArgs.message = "This message came from the source user control";

                        if (OnSend != null)
                        {
                          OnSend(this, messageArgs);
                        }
                      }  // btnSendMessage_Click
  }  // CH04UserControlCommSourceCS

  // The following class provides the definition of the custom event 
                    // arguments used as the event arguments for the message sent from this 
                    // control.  This class simply inherits from System.EventArgs and adds 
                    // a message property.
                    public class MessageEventArgs : System.EventArgs
                    {
                      private String mMessage;
                      public String message
                      {
                        get
                        {
                          return(mMessage);
                        }
                        set
                        {
                          mMessage = value;
                        }
                      }  // message
                    }  // MessageEventArgs
}

Example 4-18. Communicating between controls—destination user control (.ascx)

<%@ Control Language="vb" AutoEventWireup="false" 
      Codebehind="CH04UserControlCommDestinationVB.ascx.vb" 
      Inherits="ASPNetCookbook.VBExamples.CH04UserControlCommDestinationVB" %>
<asp:Label ID="labMessage" Runat="server">No Message Yet</asp:Label>

Example 4-19. Destination user control code-behind (.vb)

Option Explicit On 
Option Strict On
'-----------------------------------------------------------------------------
'
'   Module Name: CH04UserControlCommDestinationVB.ascx.vb
'
'   Description: This module provides the code behind for
'                CH04UserControlCommDestinationVB.ascx
'
'*****************************************************************************
Imports System

Namespace ASPNetCookbook.VBExamples
  Public MustInherit Class CH04UserControlCommDestinationVB
    Inherits System.Web.UI.UserControl

    'controls on the user control
    Protected labMessage As System.Web.UI.WebControls.Label

    '*************************************************************************
    '
    '   ROUTINE: updateLabel
    '
    '   DESCRIPTION: This routine provides the event handler that is the 
    '                recipient of the event raised by the source user control.
    '-------------------------------------------------------------------------
    Public Sub updateLabel(ByVal sender As System.Object, _
                                             ByVal e As MessageEventArgs)
                        'update the label with the message in the event arguments
                        labMessage.Text = e.message
                      End Sub  'updateLabel
  End Class  'CH04UserControlCommDestinationVB
End Namespace

Example 4-20. Destination user control code-behind (.cs)

//----------------------------------------------------------------------------
//
//   Module Name: CH04UserControlCommDestinationCS.ascx.cs
//
//   Description: This module provides the code behind for
//                CH04UserControlCommDestinationCS.ascx
//
//****************************************************************************
using System;

namespace ASPNetCookbook.CSExamples
{
  public abstract class CH04UserControlCommDestinationCS : 
                        System.Web.UI.UserControl
  {
    // controls on the user control
    protected System.Web.UI.WebControls.Label labMessage;

    //************************************************************************
    //
    //   ROUTINE: updateLabel
    //
    //   DESCRIPTION: This routine provides the event handler that is the 
    //                recipient of the event raised by the source user 
    //                control.
    //------------------------------------------------------------------------
    public void updateLabel(System.Object sender,
                                              MessageEventArgs e)
                      {
                        // update the label with the message in the event arguments
                        labMessage.Text = e.message;
                      }  // updateLabel
  }  // CH04UserControlCommDestinationCS
}

Example 4-21. Communicating between controls—main form (.aspx)

<%@ Page Language="vb" AutoEventWireup="false" 
         Codebehind="CH04UserControlCommTestVB.aspx.vb" 
         Inherits="ASPNetCookbook.VBExamples.CH04UserControlCommTestVB" %>
<%@ Register TagPrefix="ASPCookbook" TagName="SourceControl" 
                               Src="CH04UserControlCommSourceVB.ascx" %>         
                  <%@ Register TagPrefix="ASPCookbook" TagName="DestinationControl" 
                               Src="CH04UserControlCommDestinationVB.ascx" %>         
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
  <head>
    <title>User Control Communication Test</title>
    <link rel="stylesheet" href="css/ASPNetCookbook.css">
  </head>
  <body leftmargin="0" marginheight="0" marginwidth="0" topmargin="0">
    <form id="frmUCCommTest" method="post" runat="server">
      <table width="100%" cellpadding="0" cellspacing="0" border="0">
        <tr>
          <td align="center">
            <img src="images/ASPNETCookbookHeading_blue.gif">
          </td>
        </tr>
        <tr>
          <td class="dividerLine">
            <img src="images/spacer.gif" height="6" border="0"></td>
        </tr>
      </table>
      <table width="90%" align="center" border="0">
        <tr>
          <td><img src="images/spacer.gif" height="10" border="0"></td>
        </tr>
        <tr>
          <td align="center" class="PageHeading">
            User Control Communication Test (VB)
          </td>
        </tr>
        <tr>
          <td><img src="images/spacer.gif" height="10" border="0"></td>
        </tr>
        <tr>
          <td align="center">
            <table border="0">
              <tr>
                <td class="PageHeading">
                  Source User Control:
                </td>
              </tr>
              <tr>
                <td bgcolor="#ffffcc" align="center" height="75">
                  <ASPCookbook:SourceControl id="ucSource" runat="server" />
                </td>
              </tr>
              <tr>
                <td>&nbsp;</td>
              </tr>
              <tr>
                <td class="PageHeading">
                  Destination User Control:
                </td>
              </tr>
              <tr>
                <td bgcolor="#ffffcc" align="center" height="75">
                  <ASPCookbook:DestinationControl id="ucDestination" 
                                                                    runat="server" />
                </td>
              </tr>
            </table>
          </td>
        </tr>
      </table>
    </form>
  </body>
</html>

Example 4-22. Communicating between controls—main form code-behind (.vb)

Option Explicit On 
Option Strict On
'-----------------------------------------------------------------------------
'
'   Module Name: CH04UserControlCommTestVB.aspx.vb
'
'   Description: This module provides the code behind for
'                CH04UserControlCommTestVB.aspx
'
'*****************************************************************************
Namespace ASPNetCookbook.VBExamples
  Public Class CH04UserControlCommTestVB
    Inherits System.Web.UI.Page

    'controls on the form
    Protected ucSource As CH04UserControlCommSourceVB
    Protected ucDestination As CH04UserControlCommDestinationVB

    '*************************************************************************
    '
    '   ROUTINE: Page_Load
    '
    '   DESCRIPTION: This routine provides the event handler for the page load
    '                event.  It is responsible for wiring the source user
    '                control to the destination user control.
    '-------------------------------------------------------------------------
    Private Sub Page_Load(ByVal sender As System.Object, _
                                            ByVal e As System.EventArgs) _
                              Handles MyBase.Load
                        'wire the event to the destination user control handler
                        AddHandler ucSource.OnSend, AddressOf ucDestination.updateLabel

                      End Sub  'Page_Load
  End Class  'CH04UserControlCommTestVB
End Namespace

Example 4-23. Communicating between controls—main form code-behind (.cs)

//----------------------------------------------------------------------------
//
//   Module Name: CH04UserControlCommTestCS.aspx.cs
//
//   Description: This module provides the code behind for
//                CH04UserControlCommTestCS.aspx
//
//****************************************************************************
using ASPNetCookbook.CSExamples;
using System;

namespace ASPNetCookbook.CSExamples
{
  public class CH04UserControlCommTestCS : System.Web.UI.Page
  {
    // controls on the form
    protected CH04UserControlCommSourceCS ucSource;
    protected CH04UserControlCommDestinationCS ucDestination;
    
    //************************************************************************
    //
    //   ROUTINE: Page_Load
    //
    //   DESCRIPTION: This routine provides the event handler for the page 
    //                load event.  It is responsible for initializing the 
    //                controls on the page.
    //------------------------------------------------------------------------
    private void Page_Load(object sender, System.EventArgs e)
                      {
                        // wire the event to the destination user control handler
                  ucSource.OnSend += 
                    new 
                     CH04UserControlCommSourceCS.customMessageHandler(ucDestination.updateLabel);
                      }  // Page_Load
  }  // CH04UserControlCommTestCS
}

Get ASP.NET Cookbook now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.