Composite Server Control Sample

The following sample develops a composite control (Composite) that combines four ASP.NET server controls: two Textbox controls, a Label control, and a Button control. When its child Button control is clicked, Composite checks whether the sum of the two numbers entered in the text boxes equals a specified number (defined as a custom property) and raises a custom event. It also exposes the Text property of its child Label control as a top-level property.

Note that the child controls of a composite control are encapsulated. By default, they are not visible outside the parent. (A page developer could try to access the child controls using the parent's Controls collection, but because of intervening literal controls it might be difficult to obtain the index of a particular child control.)

A composite control can choose whether to expose a child control as a property and also which properties and events of its child controls to expose as top-level properties and events. When a composite control synthesizes properties from those of its child controls, it merely delegates to the child controls, as shown in the following example.

// Delegate to label, which is an instance of
// System.Web.UI.WebControls.Label.
public string Text
            {
                  get
                  {
                    EnsureChildControls();
                        return label.Text;
                  }
                  set
                  {
                    EnsureChildControls();
                        label.Text = value;
                  }
            }

Composite exposes the following public properties.

  • Number

    A custom property that allows a page developer to specify a number.

  • Text

    A property synthesized out of the Text property of the child Label control.

Composite exposes the following custom event.

  • Check

    A custom event that is raised when Composite checks whether the sum of the two numbers in the text boxes equals the value of Number. The Check event requires the custom event delegate, CheckEventHandler, and the corresponding class for event data, CheckEventArgs. For information about defining a custom event, see Defining an Event.

Also note the following features of the Composite control.

  • Composite creates its child controls in the CreateChildControls method and not in OnInit or in its constructor.
  • Composite does not expose the Click event of its Button child control. It instead handles the Click event and raises the custom event Check. If a composite control handles events raised by its child controls, it must wire the event handlers in CreateChildControls.
  • Composite implements INamingContainer to route a postback event to its child Button control.

Note that you can bubble events from the child controls up to the container and expose them as top-level events on the container. For details, see Bubbling an Event and Event Bubbling Control Sample.

The code for the composite control sample follows. To build the sample, see the instructions in Server Control Samples.

// Composite.cs.
using System;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace CustomControls 
{
      
      public class Composite : Control, INamingContainer 
      {
            private int number = 100;
            private Label label;
            
            public int Number
            {
                  get
                  {
                        return number;
                  }
                  set
                  {
                        number = value;
                  }
            }
            
            private int Sum
            {
                  get 
                  {
                        EnsureChildControls();
                        return Int32.Parse(((TextBox)Controls[1]).Text) + 
                              Int32.Parse(((TextBox)Controls[4]).Text);
                  }
                  
            }

            public string Text
            {
                  get
                  {
                    EnsureChildControls();
                        return label.Text;
                  }
                  set
                  {
                    EnsureChildControls();
                        label.Text = value;
                  }
            }
            
            
            public event CheckEventHandler Check;
            
            protected virtual void OnCheck(CheckEventArgs ce)
            {
                  if (Check != null)
                  {
                        Check(this,ce);
                  }
            }
            
            protected override void CreateChildControls() 
            {
                  
                  Controls.Add(new LiteralControl("<h3>Enter a number : "));
                  
                  TextBox box1 = new TextBox();
                  box1.Text = "0";
                  Controls.Add(box1);
                  
                  Controls.Add(new LiteralControl("</h3>"));
                  
                  Controls.Add(new LiteralControl("<h3>Enter another number : "));
                  
                  TextBox box2 = new TextBox();
                  box2.Text = "0";
                  Controls.Add(box2);
                  
                  Controls.Add(new LiteralControl("</h3>"));
                  
                  Button button1 = new Button();
                  button1.Text = "Submit";
                  Controls.Add(new LiteralControl("<br>"));
                  Controls.Add(button1);
                  button1.Click += new EventHandler(this.ButtonClicked);
                  
                  Controls.Add(new LiteralControl("<br><br>"));
                  label = new Label();
                  label.Height = 50;
                  label.Width = 500;
                  label.Text = "Click the button to see if you won.";
                  Controls.Add(label);
                  
            }
            
            protected override void OnPreRender(EventArgs e)
            {
                  ((TextBox)Controls[1]).Text = "0";
                  ((TextBox)Controls[4]).Text = "0";
            }
            
            private void ButtonClicked(Object sender, EventArgs e)
            {
                  OnCheck(new CheckEventArgs(Sum - Number));
            }
      }
}

// CheckEvent.cs.
// Contains the code for the custom event data class CheckEventArgs.
// Also defines the event handler for the Check event.
using System;

namespace CustomControls
{
      public class CheckEventArgs : EventArgs
      {
            private bool match = false;
            
            public CheckEventArgs (int difference)
            {
                  if (difference == 0)
                  {
                        match = true;
                  }
            }
            public bool Match
            {
                  get
                  {
                        return match;
                  }
            }
      }
      
      public delegate void CheckEventHandler(object sender, CheckEventArgs ce);
}
[Visual Basic]
' Composite.vb.
Imports System
Imports System.Web
Imports System.Web.UI
Imports System.Web.UI.WebControls


Namespace CustomControls
   Public Class Composite
      Inherits Control
      Implements INamingContainer
      Private _number As Integer = 100
      Private label As Label
      
      Public Property Number() As Integer
         Get
            Return _number
         End Get
         Set
            _number = value
         End Set
      End Property
      
      Private ReadOnly Property Sum() As Integer
         Get
            EnsureChildControls()
            Return Int32.Parse(CType(Controls(1), TextBox).Text) + Int32.Parse(CType(Controls(4), TextBox).Text)
         End Get
      End Property
      
      Public Property Text() As String
         Get
            EnsureChildControls()
            Return label.Text
         End Get
         Set
            EnsureChildControls()
            label.Text = value
         End Set
      End Property
      
      Public Event Check As CheckEventHandler
      
      Protected Overridable Sub OnCheck(ce As CheckEventArgs)
         RaiseEvent Check(Me, ce)
      End Sub
      
      Protected Overrides Sub CreateChildControls()
         
         Controls.Add(New LiteralControl("<h3>Enter a number : "))
         
         Dim box1 As New TextBox()
         box1.Text = "0"
         Controls.Add(box1)
         
         Controls.Add(New LiteralControl("</h3>"))
         
         Controls.Add(New LiteralControl("<h3>Enter another number : "))
         
         Dim box2 As New TextBox()
         box2.Text = "0"
         Controls.Add(box2)
         
         Controls.Add(New LiteralControl("</h3>"))
         
         Dim button1 As New Button()
         button1.Text = "Submit"
         Controls.Add(New LiteralControl("<br>"))
         Controls.Add(button1)
         AddHandler button1.Click, AddressOf Me.ButtonClicked
         
         Controls.Add(New LiteralControl("<br><br>"))
         label = New Label()
         label.Height = Unit.Pixel(50)
         label.Width = Unit.Pixel(500)
         label.Text = "Click the button to see if you won."
         Controls.Add(label)
      End Sub
      
      Protected Overrides Sub OnPreRender(e As EventArgs)
         CType(Controls(1), TextBox).Text = "0"
         CType(Controls(4), TextBox).Text = "0"
      End Sub
      
      Private Sub ButtonClicked(sender As [Object], e As EventArgs)
         OnCheck(New CheckEventArgs(Sum - Number))
      End Sub
   End Class
End Namespace

' CheckEvent.vb
' Contains the code for the custom event data class CheckEventArgs.
' Also defines the event handler for the Check event.
Imports System
Namespace CustomControls
   Public Class CheckEventArgs
      Inherits EventArgs
      Private _match As Boolean = False
      
      Public Sub New(difference As Integer)
         If difference = 0 Then
            _match = True
         End If
      End Sub
      
      Public ReadOnly Property Match() As Boolean
         Get
            Return _match
         End Get
      End Property
   End Class
   
   Public Delegate Sub CheckEventHandler(sender As Object, ce As CheckEventArgs)
End Namespace

Using a Composite control on a Page

The following sample uses the composite control Composite on an ASP.NET page.

<%@ Register TagPrefix="Custom" Namespace="CustomControls" Assembly = "CustomControls" %>
<html>
<script language="VB" runat=server>
   Private Sub Sum_Checked(sender As Object, e As CheckEventArgs)
      If e.Match = True Then
         Composite.Text = "<h2> You won a million dollars.!!!! </h2>"
      Else
         Composite.Text = "Sorry, try again. The numbers you entered don't add up to" _
            & " the hidden number."
      End If
   End Sub 
</script>
            
<body>

<h1> The Mystery Sum Game </h1><br>
                  
<form runat=server>                             
<Custom:Composite id = "Composite" OnCheck = "Sum_Checked" Number= "10" runat = server/>                                                                          
</form>                                               
</body>                                         
</html>

See Also

Composite Control vs. User Control | Composition vs. Rendering | Bubbling an Event | Event Bubbling Control Sample