Cooking with ASP.NET
Pages: 1, 2, 3
Recipe 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.

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 theOnSendevent 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
OnSendevent 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
OnSendevent'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
WithEventsis not used. (Recall that theWithEventskeyword indicates that a declared object variable refers to a class instance that can raise events.)WithEventsand 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> </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
}

