Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / VB

ASP.NET 4 in Practice - Composite Controls

4.93/5 (9 votes)
17 May 2011CPOL7 min read 38.3K  
A Chapter excerpt from ASP.NET 4 in Practice

This article is based on ASP.NET 4.0 in Practice published on May 15, 2011. It is being reproduced here by permission from Manning Publications. Manning publishes MEAP (Manning Early Access Program,) eBooks and pBooks. MEAPs are sold exclusively through Manning.com. All pBook purchases include free PDF, mobi and epub. When mobile formats become available all customers will be contacted and upgraded. Visit Manning.com for more information.

image002.jpgASP.NET 4 in Practice

By Daniele Bochicchio, Stefano Mostarda, and Marco De Sanctis

Custom controls are often created by combining existing ones, enhancing their features. Combining controls is more challenging than creating a new one from scratch. In this article from the book ASP.NET 4.0 in Practice, you’ll learn how to how to build composite controls using ASP.NET.

You may also be interested in…

Custom controls are often created by combining existing ones, enhancing their features. In most situations, this process consists of picking two or more controls and combining them to produce a single result. Knowing how to do this is important because you can reuse existing controls and add more features to simplify the use of common, recurring situations.

Combining controls is more challenging than creating a new one from scratch. When you create composite controls, you’ll encounter special problems. For example, the controls need to be wrapped, and their members need to be exposed in the corresponding control. This task is simple to perform but it’s also time consuming. The reality is that you’ll map only the most used and useful members and add the others as you need them.

The problem with this class of controls is that you’re hiding them from the outside, deciding what the external world may and may not use. For this reason, events handled internally by these controls can become a nightmare. You need to implement an event bubbling technique (to let events propagate through the control tree), or opt to define new events to expose just the existing ones outside the wrapped controls. To fully understand how all this will affect how you create a composite control, our scenario will cover how to build composite controls using ASP.NET.

Problem

Let’s suppose you need to create a special DropDownList that, in a single declaration, can be used to both insert the description and the options to be selected by the user.

By using this control, you can save a lot of time in terms of markup to be written, and you can reuse the same feature over and over in your projects.

Solution

Composite controls are generally created by deriving from CompositeControl in System.Web.UI.WebControls. This class implements a lot of the logic necessary to implement custom controls that are web controls, too—composite controls support styling, for example. If you don’t need these features, you can opt for the simple Control class from System.Web.UI. Using the Control class will ensure that the generated markup remains simple, but you’ll need to manually add the missing features that Composite-Control already provides.

Figure 1 illustrates the concept of composite controls. Whether you use the CompositeControl class or the Control class, you need to manipulate the page’s control tree and dynamically instantiate controls at runtime.

image003.gif

Figure 1 A composite control combines other controls. Externally, they’re treated as a single control that encapsulates the entire logic.

Composite controls work by combining controls together, so the controls are added using the CreateChildControls method.

The CreateChildControls method is called via a call to the EnsureChildControls method whenever a child control is needed. When you’re manipulating the control tree, you need to be careful and remember that these are controls that will be nested into the control itself and then into the page. To add a control inside another, you have to access its Controls properties and add it via the Add method, as shown in the following listing.

Listing 1 CreateChildControl contains the Nested Controls Declaration

C#

C#
public class SuperDropDownList: CompositeControl, INamingContainer
{
  protected override void CreateChildControls()
  {
    if (ChildControlsCreated)                                        #1
      return;
 
    Controls.Clear();                                                #A
 
    Controls.Add(new LiteralControl("<p>"));
 
    Label labelText = new Label();
    labelText.Text = Description;
    Controls.Add(labelText);
 
    Controls.Add(new LiteralControl(
                      string.IsNullOrEmpty(Description)?
 
                             string.Empty:": "));
    DropDownList listControl = new DropDownList();
 
    Controls.Add(listControl);
 
    Controls.Add(new LiteralControl("</p>"));
 
    ChildControlsCreated = true;                                     #1
  }
...                                                                  #B
}

VB

VB
Public Class SuperDropDownList
  Inherits CompositeControl
  Implements INamingContainer
  Protected Overrides Sub CreateChildControls()
    If ChildControlsCreated Then                                     #1
      Return
    End If
 
    Controls.Clear()                                                  #A
 
    Controls.Add(New LiteralControl("<p>"))
 
    Dim labelText As New Label()
    labelText.Text = Description
    Controls.Add(labelText)
 
    Controls.Add(New LiteralControl(If(String.IsNullOrEmpty(Description),
                                       String.Empty, ": ")))
 
    Dim listControl As New DropDownList()
    Controls.Add(listControl)
 
    Controls.Add(New LiteralControl("</p>"))
 
    ChildControlsCreated = True                                        #1
  End Sub
...                                                                    #B
End Class

#1 Avoids control creation
#A Removes existing controls
#B Continues code

As you can see in this listing, we’re basically adding some controls in order to display a DropDownList and a description. To remove unwanted controls from the control tree (which could be Literal controls that can be added in markup), we’re performing a call to Controls.Clear to reset the control tree. The code in #1 isn’t actually necessary because it’s already included by Composite-Control. Listing 1 shows how to deal with this problem when another simpler base control (as Control) is used. Look at figure 2 to see the results.

image004.gif

Figure 2 The new SuperDrop-DownList control is in action. This control combines different controls to provide a simple implementation.

We’ve omitted the declaration of the properties from listing 1 for brevity. When you need to set the properties for the inner controls, you have to use a special approach: you need to access an inner object’s property from outside the control. In these situations, the preferred way to go is shown in the following snippet:

C#

C#
public IList DataSource
{
  get
  {
    EnsureChildControls();                                          #A
    return ((DropDownList)Controls[3]).DataSource as IList;
  }
 
  set
  {
    EnsureChildControls();
    ((DropDownList)Controls[3]).DataSource = value;
  }
}

VB

VB
Public Property DataSource() As IList
  Get
    EnsureChildControls()
    Return TryCast(DirectCast(Controls(3), DropDownList).DataSource, IList)
  End Get
 
  Set
    EnsureChildControls()                                          #A
    DirectCast(Controls(3), DropDownList).DataSource = value
  End Set
End Property

#A Will call CreateChildControls

As you can see, we’re referring to the control we created in listing 1 (in this case, the DropDownList), finding it by position, and directly exposing its inner property. Because you don’t have to keep the inner property in sync (it’s automatically performed using this pattern), this example shows you the best way to handle this situation.

HOW TO AVOID REFERENCING A CONTROL BY POSITION To produce cleaner code, you can also save a reference to the controls in CreateChildControls and then refer to the controls using this syntax (instead of finding them by position).

The calls to EnsureChildControls are not only important—they’re mandatory. These calls ensure that the controls are created before we access them.

Now that the infrastructure of our control is in place, let’s take a look at how to use events in composite controls.

Events in Composite Controls

Events are used in custom controls to simplify the code necessary to handle a state. A composite control hides the child controls, so you need to propagate their events outside the container by implementing an event wrapper.

Redirecting an event is a simple technique. The event is sent outside by first intercepting it locally and then propagating it outside. Take a look at the following snippet to understand how it works. In this case, the code is worth 1,000 words.

C#

C#
public event EventHandler SelectedValueChanged;

protected void OnSelectedValueChanged(EventArgs e)
{
  if (SelectedValueChanged != null)
    SelectedValueChanged(this, e);
}

VB

VB
Public Event SelectedValueChanged As EventHandler

Protected Sub OnSelectedValueChanged(e As EventArgs)
  RaiseEvent SelectedValueChanged(Me, e)
End Sub

This snippet will expose a new event, called SelectedValueChanged, and a new OnSelectedValueChanged method, which is used to define the event handler in the markup. The last addition we need to make in order to attach the event to the inner control is to add this simple code in the CreateChildControls method, right after the DropDownList instance:

C#

C#
DropDownList listControl = new DropDownList();
listControl.SelectedIndexChanged += (object sender, EventArgs e) => {
  OnSelectedValueChanged(e);
};

VB

VB
Dim listControl as New DropDownList()
listControl.SelectedIndexChanged += Function(sender As Object,
                                             e As EventArgs) Do
     OnSelectedValueChanged(e)
End Function

This snippet ensures that when the DropDownList’s SelectedIndexChanged event is fired, our event will be fired, too. The result is that the event handler created inside the page will also be called, and our event will propagate outside the contained control.

Discussion

When you’re building composite controls, you need to pay attention to the fact that you’re not generating markup, but composing your controls, mixing them together, and manipulating the page’s control tree. This task is certainly easy to implement in a simple scenario like the one we covered here because you’re leveraging existing controls but it can also be prone to error. As you’ve learned in this scenario, you need to understand how CreateChildControls and EnsureChildControls work.

Summary

Getting started with custom controls isn’t difficult, but advanced scenarios involve a deep understanding of ASP.NET. In more simple situations, custom controls can help you avoid code duplication by implementing and supporting repetitive tasks. You can easily add PostBack and templates to every control, and implementing support for data binding isn’t all that difficult.

 

Here are some other Manning titles you might be interested in:

image005.jpg

ASP.NET MVC 2 in Action
Jeffrey Palermo, Ben Scheirman, Jimmy Bogard, Eric Hexter, and Matthew Hinze

 

image005.jpg

ASP.NET AJAX in Action
Alessandro Gallo, David Barkol, and Rama Krishna Vavilala

image007.jpg

IronRuby in Action
Ivan Porto Carrero and Adam Burmister

 

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)