Composite Web Control Example
This example shows how to create a control named Register that demonstrates the main steps required to implement a composite ASP.NET server control. A composite control uses child controls to create a user interface (UI) and to perform other logic. Since a composite control relies on child controls for its functionality, it is significantly easier to develop a composite control than to implement all of the control's functionality yourself.
In this example, the Register control uses child controls to create a user interface (UI) for entering user information to register with a Web site. The UI consists of two TextBox controls, one for entering the user's name and another to enter the user's e-mail address, and a Button control to submit the information. Register also associates RequiredFieldValidator controls with the two TextBox controls to be sure that the user enters a name and an e-mail address. The Click event of the Button control is raised as the Register control's Submit event.
The Register control relies on the built-in features of the child controls for its behavior. For example, Register relies on the TextBox controls for handling postback data, the Button control for the postback event, and the RequiredFieldValidator controls for validation.
The code for the Register control is described in the "Code Discussion" section later in this topic. When you examine the code for the Register control, you will notice that most of the code relates to creating and managing the child controls.
Code Listing for the Register Control
' Register.vbOption Strict OnImports System
Imports System.ComponentModel
Imports System.Drawing
Imports System.Security.Permissions
Imports System.Web
Imports System.Web.UI
Imports System.Web.UI.WebControls
Namespace Samples.AspNet.VB.Controls
< _
AspNetHostingPermission(SecurityAction.Demand, _
Level:=AspNetHostingPermissionLevel.Minimal), _
AspNetHostingPermission(SecurityAction.InheritanceDemand, _
Level:=AspNetHostingPermissionLevel.Minimal), _
DefaultEvent("Submit"), _
DefaultProperty("ButtonText"), _
ToolboxData("<{0}:Register runat=""server""> </{0}:Register>") _
> _
PublicClass Register
Inherits CompositeControl
Private submitButton As Button
Private nameTextBox As TextBox
Private nameLabel As Label
Private emailTextBox As TextBox
Private emailLabel As Label
Private emailValidator As RequiredFieldValidator
Private nameValidator As RequiredFieldValidator
PrivateSharedReadOnly EventSubmitKey AsNewObject()
' The following properties are delegated to child controls.
< _
Bindable(True), _
Category("Appearance"), _
DefaultValue(""), _
Description("The text to display on the button.") _
> _
PublicProperty ButtonText() AsStringGet
EnsureChildControls()
Return submitButton.Text
EndGetSet(ByVal value AsString)
EnsureChildControls()
submitButton.Text = value
EndSetEndProperty
< _
Bindable(True), _
Category("Default"), _
DefaultValue(""), _
Description("The user name.") _
> _
PublicProperty Name() AsStringGet
EnsureChildControls()
Return nameTextBox.Text
EndGetSet(ByVal value AsString)
EnsureChildControls()
nameTextBox.Text = value
EndSetEndProperty
< _
Bindable(True), _
Category("Appearance"), _
DefaultValue(""), _
Description("Error message for the name validator.") _
> _
PublicProperty NameErrorMessage() AsStringGet
EnsureChildControls()
Return nameValidator.ErrorMessage
EndGetSet(ByVal value AsString)
EnsureChildControls()
nameValidator.ErrorMessage = value
nameValidator.ToolTip = value
EndSetEndProperty
< _
Bindable(True), _
Category("Appearance"), _
DefaultValue(""), _
Description("The text for the name label.") _
> _
PublicProperty NameLabelText() AsStringGet
EnsureChildControls()
Return nameLabel.Text
EndGetSet(ByVal value AsString)
EnsureChildControls()
nameLabel.Text = value
EndSetEndProperty
< _
Bindable(True), _
Category("Default"), _
DefaultValue(""), _
Description("The e-mail address.") _
> _
PublicProperty Email() AsStringGet
EnsureChildControls()
Return emailTextBox.Text
EndGetSet(ByVal value AsString)
EnsureChildControls()
emailTextBox.Text = value
EndSetEndProperty
< _
Bindable(True), _
Category("Appearance"), _
DefaultValue(""), _
Description("Error message for the e-mail validator.") _
> _
PublicProperty EmailErrorMessage() AsStringGet
EnsureChildControls()
Return emailValidator.ErrorMessage
EndGetSet(ByVal value AsString)
EnsureChildControls()
emailValidator.ErrorMessage = value
emailValidator.ToolTip = value
EndSetEndProperty
< _
Bindable(True), _
Category("Appearance"), _
DefaultValue(""), _
Description("The text for the e-mail label.") _
> _
PublicProperty EmailLabelText() AsStringGet
EnsureChildControls()
Return emailLabel.Text
EndGetSet(ByVal value AsString)
EnsureChildControls()
emailLabel.Text = value
EndSetEndProperty
' The Submit event.
< _
Category("Action"), _
Description("Raised when the user clicks the button.") _
> _
Public Custom Event Submit As EventHandler
AddHandler(ByVal value As EventHandler)
Events.AddHandler(EventSubmitKey, value)
EndAddHandlerRemoveHandler(ByVal value As EventHandler)
Events.RemoveHandler(EventSubmitKey, value)
EndRemoveHandlerRaiseEvent(ByVal sender AsObject, _
ByVal e As System.EventArgs)
CType(Events(EventSubmitKey), _
EventHandler).Invoke(sender, e)
EndRaiseEventEndEvent
' The method that raises the Submit event.ProtectedOverridableSub OnSubmit(ByVal e As EventArgs)
Dim submitHandler As EventHandler = _
CType(Events(EventSubmitKey), EventHandler)
If submitHandler IsNotNothingThen
submitHandler(Me, e)
EndIfEndSub
' Handles the Click event of the Button and raises ' the Submit event.PrivateSub submitButton_Click(ByVal source AsObject, _
ByVal e As EventArgs)
OnSubmit(EventArgs.Empty)
EndSubProtectedOverridesSub CreateChildControls()
Controls.Clear()
nameLabel = New Label()
nameTextBox = New TextBox()
nameTextBox.ID = "nameTextBox"
nameValidator = New RequiredFieldValidator()
nameValidator.ID = "validator1"
nameValidator.ControlToValidate = nameTextBox.ID
nameValidator.Text = "Failed validation."
nameValidator.Display = ValidatorDisplay.Static
emailLabel = New Label()
emailTextBox = New TextBox()
emailTextBox.ID = "emailTextBox"
emailValidator = New RequiredFieldValidator()
emailValidator.ID = "validator2"
emailValidator.ControlToValidate = emailTextBox.ID
emailValidator.Text = "Failed validation."
emailValidator.Display = ValidatorDisplay.Static
submitButton = New Button()
submitButton.ID = "button1"AddHandler submitButton.Click, _
AddressOf submitButton_Click
Me.Controls.Add(nameLabel)
Me.Controls.Add(nameTextBox)
Me.Controls.Add(nameValidator)
Me.Controls.Add(emailLabel)
Me.Controls.Add(emailTextBox)
Me.Controls.Add(emailValidator)
Me.Controls.Add(submitButton)
EndSubProtectedOverridesSub Render(ByVal writer As HtmlTextWriter)
AddAttributesToRender(writer)
writer.AddAttribute(HtmlTextWriterAttribute.Cellpadding, _
"1", False)
writer.RenderBeginTag(HtmlTextWriterTag.Table)
writer.RenderBeginTag(HtmlTextWriterTag.Tr)
writer.RenderBeginTag(HtmlTextWriterTag.Td)
nameLabel.RenderControl(writer)
writer.RenderEndTag()
writer.RenderBeginTag(HtmlTextWriterTag.Td)
nameTextBox.RenderControl(writer)
writer.RenderEndTag()
writer.RenderBeginTag(HtmlTextWriterTag.Td)
nameValidator.RenderControl(writer)
writer.RenderEndTag()
writer.RenderEndTag()
writer.RenderBeginTag(HtmlTextWriterTag.Tr)
writer.RenderBeginTag(HtmlTextWriterTag.Td)
emailLabel.RenderControl(writer)
writer.RenderEndTag()
writer.RenderBeginTag(HtmlTextWriterTag.Td)
emailTextBox.RenderControl(writer)
writer.RenderEndTag()
writer.RenderBeginTag(HtmlTextWriterTag.Td)
emailValidator.RenderControl(writer)
writer.RenderEndTag()
writer.RenderEndTag()
writer.RenderBeginTag(HtmlTextWriterTag.Tr)
writer.AddAttribute(HtmlTextWriterAttribute.Colspan, _
"2", False)
writer.AddAttribute(HtmlTextWriterAttribute.Align, _
"right", False)
writer.RenderBeginTag(HtmlTextWriterTag.Td)
submitButton.RenderControl(writer)
writer.RenderEndTag()
writer.RenderBeginTag(HtmlTextWriterTag.Td)
writer.Write(" ")
writer.RenderEndTag()
writer.RenderEndTag()
writer.RenderEndTag()
EndSubProtectedOverridesSub RecreateChildControls()
EnsureChildControls()
EndSubEndClassEndNamespace
// Register.csusing System;
using System.ComponentModel;
using System.Drawing;
using System.Security.Permissions;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace Samples.AspNet.CS.Controls
{
[
AspNetHostingPermission(SecurityAction.Demand,
Level = AspNetHostingPermissionLevel.Minimal),
AspNetHostingPermission(SecurityAction.InheritanceDemand,
Level=AspNetHostingPermissionLevel.Minimal),
DefaultEvent("Submit"),
DefaultProperty("ButtonText"),
ToolboxData("<{0}:Register runat=\"server\"> </{0}:Register>"),
]
publicclass Register : CompositeControl
{
private Button submitButton;
private TextBox nameTextBox;
private Label nameLabel;
private TextBox emailTextBox;
private Label emailLabel;
private RequiredFieldValidator emailValidator;
private RequiredFieldValidator nameValidator;
privatestaticreadonlyobject EventSubmitKey =
newobject();
// The following properties are delegated to // child controls.
[
Bindable(true),
Category("Appearance"),
DefaultValue(""),
Description("The text to display on the button.")
]
publicstring ButtonText
{
get
{
EnsureChildControls();
return submitButton.Text;
}
set
{
EnsureChildControls();
submitButton.Text = value;
}
}
[
Bindable(true),
Category("Default"),
DefaultValue(""),
Description("The user name.")
]
publicstring Name
{
get
{
EnsureChildControls();
return nameTextBox.Text;
}
set
{
EnsureChildControls();
nameTextBox.Text = value;
}
}
[
Bindable(true),
Category("Appearance"),
DefaultValue(""),
Description(
"Error message for the name validator.")
]
publicstring NameErrorMessage
{
get
{
EnsureChildControls();
return nameValidator.ErrorMessage;
}
set
{
EnsureChildControls();
nameValidator.ErrorMessage = value;
nameValidator.ToolTip = value;
}
}
[
Bindable(true),
Category("Appearance"),
DefaultValue(""),
Description("The text for the name label.")
]
publicstring NameLabelText
{
get
{
EnsureChildControls();
return nameLabel.Text;
}
set
{
EnsureChildControls();
nameLabel.Text = value;
}
}
[
Bindable(true),
Category("Default"),
DefaultValue(""),
Description("The e-mail address.")
]
publicstring Email
{
get
{
EnsureChildControls();
return emailTextBox.Text;
}
set
{
EnsureChildControls();
emailTextBox.Text = value;
}
}
[
Bindable(true),
Category("Appearance"),
DefaultValue(""),
Description(
"Error message for the e-mail validator.")
]
publicstring EmailErrorMessage
{
get
{
EnsureChildControls();
return emailValidator.ErrorMessage;
}
set
{
EnsureChildControls();
emailValidator.ErrorMessage = value;
emailValidator.ToolTip = value;
}
}
[
Bindable(true),
Category("Appearance"),
DefaultValue(""),
Description("The text for the e-mail label.")
]
publicstring EmailLabelText
{
get
{
EnsureChildControls();
return emailLabel.Text;
}
set
{
EnsureChildControls();
emailLabel.Text = value;
}
}
// The Submit event.
[
Category("Action"),
Description("Raised when the user clicks the button.")
]
publicevent EventHandler Submit
{
add
{
Events.AddHandler(EventSubmitKey, value);
}
remove
{
Events.RemoveHandler(EventSubmitKey, value);
}
}
// The method that raises the Submit event.protectedvirtualvoid OnSubmit(EventArgs e)
{
EventHandler SubmitHandler =
(EventHandler)Events[EventSubmitKey];
if (SubmitHandler != null)
{
SubmitHandler(this, e);
}
}
// Handles the Click event of the Button and raises// the Submit event.privatevoid _button_Click(object source, EventArgs e)
{
OnSubmit(EventArgs.Empty);
}
protectedoverridevoid RecreateChildControls()
{
EnsureChildControls();
}
protectedoverridevoid CreateChildControls()
{
Controls.Clear();
nameLabel = new Label();
nameTextBox = new TextBox();
nameTextBox.ID = "nameTextBox";
nameValidator = new RequiredFieldValidator();
nameValidator.ID = "validator1";
nameValidator.ControlToValidate = nameTextBox.ID;
nameValidator.Text = "Failed validation.";
nameValidator.Display = ValidatorDisplay.Static;
emailLabel = new Label();
emailTextBox = new TextBox();
emailTextBox.ID = "emailTextBox";
emailValidator = new RequiredFieldValidator();
emailValidator.ID = "validator2";
emailValidator.ControlToValidate =
emailTextBox.ID;
emailValidator.Text = "Failed validation.";
emailValidator.Display = ValidatorDisplay.Static;
submitButton = new Button();
submitButton.ID = "button1";
submitButton.Click
+= new EventHandler(_button_Click);
this.Controls.Add(nameLabel);
this.Controls.Add(nameTextBox);
this.Controls.Add(nameValidator);
this.Controls.Add(emailLabel);
this.Controls.Add(emailTextBox);
this.Controls.Add(emailValidator);
this.Controls.Add(submitButton);
}
protectedoverridevoid Render(HtmlTextWriter writer)
{
AddAttributesToRender(writer);
writer.AddAttribute(
HtmlTextWriterAttribute.Cellpadding,
"1", false);
writer.RenderBeginTag(HtmlTextWriterTag.Table);
writer.RenderBeginTag(HtmlTextWriterTag.Tr);
writer.RenderBeginTag(HtmlTextWriterTag.Td);
nameLabel.RenderControl(writer);
writer.RenderEndTag();
writer.RenderBeginTag(HtmlTextWriterTag.Td);
nameTextBox.RenderControl(writer);
writer.RenderEndTag();
writer.RenderBeginTag(HtmlTextWriterTag.Td);
nameValidator.RenderControl(writer);
writer.RenderEndTag();
writer.RenderEndTag();
writer.RenderBeginTag(HtmlTextWriterTag.Tr);
writer.RenderBeginTag(HtmlTextWriterTag.Td);
emailLabel.RenderControl(writer);
writer.RenderEndTag();
writer.RenderBeginTag(HtmlTextWriterTag.Td);
emailTextBox.RenderControl(writer);
writer.RenderEndTag();
writer.RenderBeginTag(HtmlTextWriterTag.Td);
emailValidator.RenderControl(writer);
writer.RenderEndTag();
writer.RenderEndTag();
writer.RenderBeginTag(HtmlTextWriterTag.Tr);
writer.AddAttribute(
HtmlTextWriterAttribute.Colspan,
"2", false);
writer.AddAttribute(
HtmlTextWriterAttribute.Align,
"right", false);
writer.RenderBeginTag(HtmlTextWriterTag.Td);
submitButton.RenderControl(writer);
writer.RenderEndTag();
writer.RenderBeginTag(HtmlTextWriterTag.Td);
writer.Write(" ");
writer.RenderEndTag();
writer.RenderEndTag();
writer.RenderEndTag();
}
}
}
Code Discussion
The Register control performs the following tasks that are common to all composite controls:
Deriving from the CompositeControl class.
Creating child controls by overriding the CreateChildControls method.
The CompositeControl class implements the common functionality needed by all composite controls. It also has an associated control designer that ensures that child controls are displayed on the design surface in a visual designer.
Note
The CompositeControl class is new in ASP.NET 2.0. If you created custom controls in ASP.NET version 1.0 or 1.1, you had to implement the INamingContainer interface to create a new naming scope for child controls. In addition, you had to override the Controls property and invoke the EnsureChildControls method. In ASP.NET 2.0, these and other steps are performed by the CompositeControl class.
You should create the child controls in the CreateChildControls method and not in OnInit or another life cycle phase. The server control architecture relies on calls to CreateChildControls whenever the Controls collection is needed, such as during data binding (if applicable).
The Register control also illustrates how to expose properties of child controls as top-level properties. This is useful when you want to allow the page developer to access the properties of a child control. For example, Register exposes the Text property of the Button child control as its own ButtonText property, as shown in the following code drawn from the example:
< _
Bindable(True), _
Category("Appearance"), _
DefaultValue(""), _
Description("The text to display on the button.") _
> _
PublicProperty ButtonText() AsStringGet
EnsureChildControls()
Return submitButton.Text
EndGetSet(ByVal value AsString)
EnsureChildControls()
submitButton.Text = value
EndSetEndProperty
[
Bindable(true),
Category("Appearance"),
DefaultValue(""),
Description("The text to display on the button.")
]
publicstring ButtonText
{
get
{
EnsureChildControls();
return submitButton.Text;
}
set
{
EnsureChildControls();
submitButton.Text = value;
}
}
The Register control does not store ButtonText or other properties it delegates to child controls in view state because the child controls use view state for storing these properties. For information on how to manage the state of properties that are not delegated to child controls, see Server Control Properties Example. That topic contains information about using the ViewState dictionary for storing simple properties and implementing custom state management for properties that have subproperties.
The Register control shows how to raise an event from the handler of a child control's event. The Register control defines an event named Submit and raises the event by attaching a handler to the Click event of the Button child control.
Test Page for the Register Control
The following example shows an ASP.NET Web page uses the Register control.
<%@ Page Language="VB"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN""http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<script runat="server">
Sub Page_Load(ByVal sender AsObject, ByVal e As EventArgs)
Label1.Visible = FalseEndSubSub Register_Submit(ByVal sender AsObject, ByVal e As EventArgs)
' The application developer can implement ' logic here to enter registration data into ' a database or write a cookie ' on the user's computer. ' This example merely writes a message ' using the Label control on the page.
Label1.Text = String.Format( _
"Thank you, {0}! You are registered.", Register1.Name)
Label1.Visible = True
Register1.Visible = FalseEndSub
</script>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head id="Head1" runat="server">
<title>
Register Control Test Page
</title>
</head>
<body>
<form id="form1" runat="server">
<aspSample:Register ButtonText="Submit"
OnSubmit="Register_Submit" ID="Register1"
Runat="server" NameLabelText="Name:" EmailLabelText="Email:"
EmailErrorMessage="You must enter your e-mail address."
NameErrorMessage="You must enter your name." />
<br />
<asp:Label ID="Label1" Runat="server" Text="Label">
</asp:Label>
<asp:ValidationSummary ID="ValidationSummary1"
Runat="server" DisplayMode="List" />
</form>
</body>
</html>
<%@ Page Language="C#"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN""http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<script runat="server">
void Page_Load(object sender, EventArgs e)
{
Label1.Visible = false;
}
void Register_Submit(object sender, EventArgs e)
{
// The application developer can implement// logic here to enter registration data into// a database or write a cookie// on the user's computer.// This example merely writes a message// using the Label control on the page.
Label1.Text = String.Format(
"Thank you, {0}! You are registered.",Register1.Name);
Label1.Visible = true;
Register1.Visible = false;
}
</script>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head id="Head1" runat="server">
<title>
Register Control Test Page
</title>
</head>
<body>
<form id="form1" runat="server">
<aspSample:Register ButtonText="Register"
OnSubmit="Register_Submit" ID="Register1"
Runat="server" NameLabelText="Name:"
EmailLabelText="Email:"
EmailErrorMessage="You must enter your e-mail address."
NameErrorMessage="You must enter your name." />
<br />
<asp:Label ID="Label1" Runat="server" Text="Label">
</asp:Label>
<asp:ValidationSummary ID="ValidationSummary1"
Runat="server" DisplayMode="List" />
</form>
</body>
</html>
Building and Using the Example
For information about compiling and using the custom control examples, see Building the Custom Server Control Examples.