Build a Composite Control

User controls are sufficient for building simple UI collections of controls, but they have several limitations that make them unsuitable for commercial products, for repeated use in multiple projects, and for handing off to HTML authors.

■ User controls must reside in the same project as the Web Forms that use them. That's a serious limitation because it means you can't create a single User control and then reference it in projects; instead, you must have multiple copies of the User control files—one for each project where you want to use it.

■ User control properties don't appear in the property browser; the control users must know what they are in advance and write them into the HTML tag manually or set them via code. That's OK for controls you build for your own use, but it isn't suitable for controls you build for others.

■ User controls in design mode don't look like they do at runtime; therefore, from a designer's viewpoint, they can be difficult to align, size, and control.

Instead, ASP.NET supports two other types of custom controls, termed Composite controls and (confusingly) Custom Server controls. These types of controls are exactly the same as the ASP.NET intrinsic Web Server controls, except that you must define them yourself, in code.

A Composite control, unlike a User control, compiles to an assembly and has design-time support for letting control users set properties in the Properties window and move and size the control at design time. A Composite control combines controls by creating the child controls programmatically. To the control user, a Composite control, like a User control, is a single entity. The user of a Composite control adds one item to a Web Form, but that single action may create any number of controls contained inside the Composite control. The user may or may not have direct access to the control properties; as the control author, you can decide. The ASP.NET framework contains several examples; the data-bound controls such as Repeater, DataList, and DataGrid are Composite controls that contain child controls.

One common task when building input forms of almost any kind is the addition of paired Label and TextBox controls (see Figure 23.3). You can create the Label and the TextBox as a single composite control.

Figure 23.3: The LabeledTextBox Composite control default interface

In this section, you'll build a Composite control named LabeledTextBox. A Composite control consists of a single class in which you must do the following: ■

Figure 23.3: The LabeledTextBox Composite control default interface

In this section, you'll build a Composite control named LabeledTextBox. A Composite control consists of a single class in which you must do the following: ■

Inherit the Control or WebControl class Override the CreateChildControls method Implement the INamingContainer interface Optionally, override the Render method The base Control or WebControl class handles events such as rendering ASP.NET or HTML controls,

manages ViewState for the child controls, and supports ASP.NET events. The CreateChild-Controls method fires when the framework signals the control to create its child controls. The method fires only once for any instance of the control.

The INamingContainer interface tells the framework to create a new naming scope, ensuring that the child controls within the naming scope have unique names. In other words, if there is already a ListBox on the Web Form, the framework encounters your control and changes the naming scope. Then when you add the child ListBoxes, the new name scope ensures that they aren't named ListBox1 and ListBox2, which would otherwise be the default names.

You can build the Composite control in your Web application project namespace, but it's usually best to create generic controls in a separate namespace because you may need them again, and it's much easier to reference and load them if they're in a separate control collection namespace. Therefore, you'll build the LabeledTextBox control in the CSharpASPCustom project you created in the preceding section of this book.

To begin building the control, load the CSharpASPCustom project and add a new class to the project. Add using statements to include these namespaces:

■ System.Web.UI.WebControls

Create the class name—this is the name that will appear in the Toolbox for control consumers. In this case, name the class LabeledTextBox. You can inherit from either the System.Web.UI.Control or the System.Web.UI.WebControls.WebControl base classes. To choose, consider how much support your control needs at design time. If you're building a visible control, you might want to expose UI properties such as Width, Height, and Font. In that case, inherit from WebControl; otherwise, just inherit from the Control class. Implement the INamingContainer interface. Because the LabeledTextBox control is a visible control that designers should be able to size and place on the page, it inherits from the WebControl class.

The code for the LabeledTextBox control resides in the CSharpASPCustom project.

using System;

using System.Web;

using System.Web.UI;

using System.Web.UI.WebControls;

namespace CSharpASPCustom {

public class LabeledTextBox :

System.Web.UI.WebControls.WebControl, INamingContainer

// more code here

Although you don't have to predefine variables for the child controls, it's convenient and prevents you from having to refer to them later using an index into your control's child controls. In this project, there are three controls: two Labels and one TextBox:

private System.Web.UI.WebControls.Label lbl = null; private System.Web.UI.WebControls.Label lblSpacer = null; private System.Web.UI.WebControls.TextBox tx = null;

The lbl control displays the label text for the control. The lblSpacer control acts as a spacer between the Label containing the text and the TextBox. The TextBox lets users input data.

You override the CreateChildControls method to create the controls:

protected override void CreateChildControls() { lblSpacer = new Label(); lblSpacer.Height = Unit.Pixel(25); lbl = new Label();

lbl.Height = Unit.Pixel(25); tx = new TextBox(); tx.Height = Unit.Pixel(25); Controls.Add(lbl); lblSpacer.Text = ""; Controls.Add(lblSpacer); Controls.Add(tx);

You can expose custom properties by adding public properties to your control. Public control properties appear in the Properties window by default. For example, the LabeledTextBox class exposes five public properties. Their names are self-explanatory: Text, LabelText, LabelWidth, TextWidth, and SpaceBetweenControls. The call to the EnsureChildControls method in each property setting ensures that the child controls exist—if you forget this call, you'll get an error stating that the control doesn't exist when it tries to site itself.

public String Text { get {

EnsureChildControls(); return tx.Text;

EnsureChildControls(); tx.Text = value;

public String LabelText { get {

EnsureChildControls(); return lbl.Text;

EnsureChildControls(); lbl.Text = value;

public Unit TextBoxWidth { get {

EnsureChildControls(); return tx.Width;

EnsureChildControls(); tx.Width = value;

public Unit LabelWidth { get {

EnsureChildControls(); return lbl.Width;

EnsureChildControls(); lbl.Width = value;

public Unit SpaceBetweenControls { get {

EnsureChildControls(); return lblSpacer.Width;

EnsureChildControls(); lblSpacer.Width = value;

Exposing the LabelWidth, TextBoxWidth, and SpaceBetweenControls properties as the type System.Unit lets users enter any of several specific unit types for that property. The ASP.NET engine translates the string representation to the appropriate value. For example, 30px, 1in, and 10pt are all valid Unit measurements. Because the control inherits from System.Web.UI.WebControls.WebControl, it also inherits several public properties such as Width, Height, Font, BackColor, ForeColor, BorderStyle, and a few others. Listing 23.4 shows the full code.

Listing 23.4: The LabeledTextBox Composite Control Code

(CSharpASPCustom.LabeledTextBox.cs)

using System;

using System.Web;

using System.Web.UI;

using System.Web.UI.WebControls;

namespace CSharpASPCustom {

[ToolboxData("<{0}:LabeledTextBox runat='server'

style='position:absolute; width:300px; height: 30px' Text='Text' LabelText='<b>Label</b>:' SpaceBetweenControls='5px'/>")] public class LabeledTextBox :

System.Web.UI.WebControls.WebControl, INamingContainer

private System.Web.UI.WebControls.Label lbl = null; private System.Web.UI.WebControls.Label lblSpacer = null; private System.Web.UI.WebControls.TextBox tx = null;

public LabeledTextBox() { }

public String Text { get {

EnsureChildControls(); return tx.Text;

EnsureChildControls(); tx.Text = value;

public String LabelText { get {

EnsureChildControls(); return lbl.Text;

EnsureChildControls(); lbl.Text = value;

public Unit TextBoxWidth { get {

EnsureChildControls(); return tx.Width;

EnsureChildControls(); tx.Width = value;

public Unit LabelWidth { get {

EnsureChildControls();

return lbl.Width;

EnsureChildControls(); lbl.Width = value;

public Unit SpaceBetweenControls { get {

EnsureChildControls(); return lblSpacer.Width;

EnsureChildControls(); lblSpacer.Width = value;

protected override void CreateChildControls() { tx = new TextBox(); lbl = new Label(); lblSpacer = new Label(); lbl.Height = Unit.Pixel(25); lbl.Text = "Label"; tx.Height = Unit.Pixel(25); tx.Width = Unit.Pixel(200); tx.Text = "Text"; Controls.Add(lbl); lblSpacer.Text = ""; Controls.Add(lblSpacer); Controls.Add(tx);

The full code listing contains one item I haven't yet explained—the ToolboxData attribute.

[ToolboxData("<{0}:LabeledTextBox runat='server'

style='position:absolute; width:300px; height: 30px' Text='Text' LabelText='<b>Label</b>:' SpaceBetweenControls='5px'/>")]

By default, when you drag and drop a control from the Toolbox to the design surface, VS.NET inserts a "blank" tag—in other words, the default control tag won't have the properties you might want. You'll find that even setting the properties in code won't help because those properties won't appear in the designer, but the properties you set in the ToolboxData attribute will appear. The value of the ToolboxData attribute is the full tag that you want the control to have. VS.NET replaces the {0} placeholder at the front of the tag with the tag prefix for the control class. The ToolboxData attribute for the LabeledTextBox control sets every custom property to a default value. Build the control and save it. Be sure to compile the project before attempting to add an instance of the control to the project.

Was this article helpful?

0 0

Post a comment