Composite Controls





Composite Controls

Many controls contain other controls as part of their definition—these are termed composite controls. By embedding other controls within them, custom controls can be used to define "chunks" of forms that can potentially be reused from many different pages, complete with their own properties, events, and methods. Composite controls are built by creating child controls and adding them to the Controls collection of the parent control. All creation of child controls should take place in an override of the virtual function CreateChildControls. This function is inherited from the Control base class and is called at the correct time to create child controls (after the Load event, just before rendering).

1 Creating Child Controls

Listing 8-30 shows a simple composite control that implements a calculator by using three TextBox controls, a Button control, and some Literal Controls intermingled. A LiteralControl is a simple control that renders its Text property and is especially useful when building composite controls for properly laying out the child controls. This control also shows an example of hooking up an event handler to a child control.

-30 Composite Control Example
Public Class CalcComposite
  Inherits Control
  Implements INamingContainer

    Private _operand1 As TextBox
    Private _operand2 As TextBox
    Private _result   As TextBox

    Private Sub OnCalculate(sender As Object, _
                            e As EventArgs)
      Dim res As Integer
      res = Convert.ToInt32(_operand1.Text) + _
            Convert.ToInt32(_operand2.Text)
      _result.Text = res.ToString()
    End Sub

    Public ReadOnly Property Result As Integer
      Get
        Return Convert.ToInt32(_result.Text)
      End Get
    End Property

    Protected Overrides Sub CreateChildControls()
      _operand1 = new TextBox()
      _operand1.ID = "Op1"
      _operand2 = new TextBox()
      _operand2.ID = "Op2"
      _result   = new TextBox()
      _result.ID = "Res"

      Controls.Add(_operand1)
      Controls.Add(new LiteralControl(" + "))
      Controls.Add(_operand2)
      Controls.Add(new LiteralControl(" = "))
      Controls.Add(_result)
      Controls.Add(new LiteralControl("<br/>"))

      Dim calculate As Button = new Button()
      calculate.Text = "Calculate"
      AddHandler calculate.Click, _
                 New EventHandler(AddressOf Me.OnCalculate)
      Controls.Add(calculate)
    End Sub

  End Class

Notice also that the control shown in Listing 8-30 implements the INamingContainer interface. This is a marker interface (one with no methods) that indicates to the containing page that this control has child controls that may need to be in a separate namespace. If there is more than one instance of a composite control on a given page, it is important that the child controls of each composite control not have ID clashes. Anytime you have child controls in a custom control, you should be sure to add support for INamingContainer to avoid ID clashes in the rendered HTML.

You may find that in your composite control, you would like to manipulate some of your child controls during the Load event of your control. The CreateChildControls method is not called until after the Load event has fired, however, so if you attempt to access any child controls within a Load handler, you will find none. To guarantee that your child controls have been created, you can always call EnsureChildControls(). This function checks to see if the CreateChildControls function has been called yet (by checking the ChildControlsCreated Boolean), and if not, calls it for you. For example, the composite control shown in Listing 8-31 explicitly calls EnsureChildControls from within its Load handler before it tries to set one of the children's values.

-31 Calling EnsureChildControls
Public Class CalcComposite
  Inherits Control
  Implements INamingContainer

  Private _operand1 As TextBox
  Private _operand2 As TextBox
  Private _result   As TextBox

  Protected Sub Page_Load(src As Object, e As EventArgs)
    EnsureChildControls()
    _operand1.Text = "41"
  End Sub

  Protected Overrides Sub CreateChildControls()
    _operand1 = new TextBox()
    _operand2 = new TextBox()
    _result   = new TextBox()
    '...
  End Sub
End Class

With a composite control, it may also be useful to retrieve a child control dynamically. This is easily done with the FindControl method, which takes the string identifier of the control and returns a reference to the control.

2 Custom Events

For a custom control to truly provide all the attributes of standard Web controls, it must be able to define and propagate events. You define events in a custom control by adding a public event data member of delegate type EventHandler. A client then attaches a method to the event handler, and it is up to the control to invoke that method whenever the event logically occurs. Listing 8-32 shows a modified version of our CalcComposite control with a custom event added. Note that it declares a public EventHandler member called MagicNumber to which clients can hook delegates. In our example, the EventHandler is invoked whenever the user calculates the value 42 with the calculator. Listing 8-33 shows a sample .aspx page that traps the MagicNumber event and populates a label in response.

-32 Control with Custom Event Example
Public Class CalcComposite
  Inherits Control
  Implements INamingContainer

    ' other members not shown

  Public Event MagicNumber As EventHandler

  Private Sub OnCalculate(sender As Object, _
                          e As EventArgs)
    Dim res As Integer
    res = Convert.ToInt32(_operand1.Text) + _
              Convert.ToInt32(_operand2.Text)
    _result.Text = res.ToString()
    If res = 42 Then
      RaiseEvent MagicNumber(Me, EventArgs.Empty)
    End If
  End Sub

  Protected Overrides Sub CreateChildControls()
    'Other control creation not shown...

    Dim calculate As Button = new Button()
    calculate.Text = "Calculate"
    AddHandler calculate.Click, _
               New EventHandler(AddressOf Me.OnCalculate)
    Controls.Add(calculate)
  End Sub
End Class
-33 Sample Custom Event Client
<%@ Page Language="VB" %>
<%@ Register TagPrefix="eadn"
             Namespace="EssentialAspDotNet.CustomControls"
             Assembly="CalcComposite" %>

<html>
<script runat=server>
Private Sub MyCtrl_OnMagicNumber(src As Object, _
                                 e As EventArgs)
  MagicNumberLabel.Text = "Magic number calculated!!"
End Sub
</script>
<body>
  <form runat=server>
    <asp:Label id=MagicNumberLabel runat=server />
    <eadn:SimpleComposite id="MyCtrl"
       OnMagicNumber="MyCtrl_OnMagicNumber" runat=server />
  </form>
</body>
</html>

     Python   SQL   Java   php   Perl 
     game development   web development   internet   *nix   graphics   hardware 
     telecommunications   C++ 
     Flash   Active Directory   Windows