You must answer two questions before writing a custom control:
The two basic types of controls are fully rendered and composite controls. When you build a fully rendered control, you start from scratch. You specify all the HTML content that the control renders to the browser. When you create a composite control, on the other hand, you build a new control from existing controls. For example, you can create a composite AddressForm control from existing TextBox and RequiredFieldValidator controls. When you create a composite control, you bundle together existing controls as a new control. The second question that you must address is the choice of the base control for your new control. You can inherit a new control from any existing ASP.NET control. For example, if you want to create a better GridView control, then you can inherit a new control from the GridView control and add additional properties and methods to your custom GridView control. Typically, when building a basic control, you inherit your new control from one of the following base classes:
The CompositeControl class inherits from the WebControl class, which inherits from the Control class. Each of these base classes adds additional functionality. The base class for all controls in the ASP.NET Framework is the System.Web.UI.Control class. Every control, including the TextBox and GridView controls, ultimately derives from this control. This means that all the properties, methods, and events of the System.Web.UI.Control class are shared by all controls in the Framework. All Web controls inherit from the base System.Web.UI.WebControls.WebControl class. The difference between the Control class and WebControl class is that controls that derive from the WebControl class always have opening and closing tags. Because a WebControl has an opening and closing tag, you also get more formatting options. For example, the WebControl class includes BackColor, Font, and ForeColor properties. For example, the ASP.NET Literal control inherits from the base Control class, whereas the Label control inherits from the base WebControl class. The Repeater control inherits from the base Control class, whereas the GridView control (ultimately) inherits from the WebControl class. Finally, the System.Web.UI.WebControls.CompositeControl is new in the ASP.NET 2.0 Framework. You should use this class as the base class for any composite control. The CompositeControl automatically creates a naming container for its child controls. It also includes an overridden Controls property that forces child controls to appear in Design view. Building Fully Rendered ControlsLet's start by creating a simple fully rendered control. When you create a fully rendered control, you take on the responsibility of specifying all the HTML content that the control renders to the browser. The file in Listing 31.1 contains a fully rendered control that derives from the base Control class. Listing 31.1. FullyRenderedControl.vb
Note Add the control in Listing 31.1 to your App_Code folder. Any code added to the App_Code folder is compiled dynamically. The control in Listing 31.1 inherits from the base Control class, overriding the base class Render() method. The control simply displays whatever value that you assign to its Text property. The value of the Text property is written to the browser with the HtmlTextWriter class's Write() method. The file in Listing 31.2 illustrates how you can use the new control in a page. Listing 31.2. ShowFullyRenderedControl.aspx
Note In Listing 31.2, the custom control is registered in the page through use of the <%@ Register %> directive. Alternatively, you can register the control for an entire website by registering the control in the <pages> section of the web configuration file. If you open the page in Listing 31.2 in a browser and select View Source, you can see the HTML rendered by the control. The control simply renders the string "Hello World!". Rather than inherit from the base Control class, you can create a fully rendered control by inheriting a new control from the base WebControl class. When inheriting from the WebControl class, you override the RenderContents() method instead of the Render() method. For example, the control in Listing 31.3 contains a simple fully rendered control that inherits from the WebControl class. Listing 31.3. FullyRenderedWebControl.vb
The page in Listing 31.4 illustrates how you can use the new control (see Figure 31.1). Notice that the BackColor, BorderStyle, and Font properties are set. Because the control in Listing 31.3 derives from the base WebControl class, you get these properties for free. Figure 31.1. Displaying a fully rendered WebControl.Listing 31.4. ShowFullyRenderedWebControl.aspx
After opening the page in Listing 31.4, if you select View Source in your browser, you can see the rendered output of the control. It looks like this:
A WebControl, unlike a control, renders an enclosing <span> tag by default. Understanding the HtmlTextWriter ClassWhen you create a fully rendered control, you use the HtmlTextWriter class to write the HTML content to the browser. The HtmlTextWriter class was specifically designed to make it easier to render HTML. Here is a partial list of the methods supported by this class:
You can call the AddAttribute() or the AddStyleAttribute() method as many times as you please before calling RenderBeginTag(). When you call RenderBeginTag(), all the attributes are added to the opening HTML tag. The methods of the HtmlTextWriter class can use the following enumerations:
When using the methods of the HtmlTextWriter class, you should strive to use these enumerations to represent HTML tags and attributes. If a particular tag or attribute is missing from one of the enumerations, you can pass a string value instead. For example, the control in Listing 31.5 renders a table of HTML colors by using an HTML table (see Figure 31.2). Notice that the RenderContents() method takes advantage of the methods of the HtmlTextWriter class to render the HTML table. Figure 31.2. Displaying a table of HTML colors.Listing 31.5. ColorTable.vb
You should notice a number of things about the control in Listing 31.5. First, notice that the AddAttribute() method is called to add the table border attribute. When the RenderBeginTag() method is called, the table border attribute is added to the opening table tag. Furthermore, notice that you do not specify the tag when calling the RenderEndTag() method. This method automatically closes the last tag opened with the RenderBeginTag() method. Note The CD that accompanies this book includes a ShowColorTable.aspx page that you can open in your browser to view the rendered output of the ColorTable control. The control in Listing 31.6, the DropShadow control, illustrates how you can use the AddStyleAttribute() method of the HtmlTextWriter class to add Cascading Style Sheet attributes to an HTML tag. Listing 31.6. DropShadow.vb
The control in Listing 31.6 renders a drop shadow behind whatever text you assign to the control's Text property (see Figure 31.3). The drop shadow is created with the help of an Internet Explorer DropShadow filter. Figure 31.3. Displaying a drop shadow with the DropShadow control.Notice that the Filter attribute is added to the <div> tag with a call to the AddStyleAttribute() method. The AddStyleAttribute() method works just like the AddAttribute() method, except that the AddStyleAttribute() method adds a CSS attribute instead of an HTML attribute. Web Standards Note Filters are an Internet Explorer extension to the Cascading Style Sheet standard. They don't work with Firefox or Opera. Firefox has its own extensions to Cascading Style Sheets with its -moz style rules. Specifying the Containing WebControl TagBy default, a WebControl renders an HTML <span> tag around its contents. You can specify a different tag by overriding the WebControl's TagKey property. For example, the control in Listing 31.7 renders its contents within an HTML <div> tag. Listing 31.7. Glow.vb
The control in Listing 31.7 displays a glowing effect around any text that you assign to its Text property. The control takes advantage of the Internet Explorer Glow filter to create the glow effect (see Figure 31.4). Figure 31.4. Displaying glowing text with the Glow control.Notice that the control overrides the base WebControl's TagKey property. Because the overridden property returns a <div> tag, the WebControl renders a <div> tag. Note There are several methods you can use to modify the tag rendered by a WebControl. You can override the TagName property instead of the TagKey property. The TagName property enables you to specify an arbitrary string for the tag. (It doesn't limit you to the HtmlTextWriterTag enumeration.) You also can specify the tag rendered by a WebControl in the WebControl's constructor. Finally, you can override a WebControl's RenderBeginTag() and RenderEndTag() methods and completely customize the opening and closing tags. Furthermore, you should notice that the control in Listing 31.7 overrides the AddAttributesToRender() method. If you override this method, then you can add HTML or CSS attributes to the opening HTML tag rendered by the control. When overriding this method, be careful to call the base AddAttributesToRender() method or the standard control attributes, such as the control ID, won't be rendered. Building Composite ControlsIf you don't want to start from scratch when building a custom control, you can build a composite control. When you create a composite control, you create a new control from existing controls. Every ASP.NET control has a Controls property that represents all of its child controls. If you add child controls to a control, then the child controls are automatically rendered when the parent control is rendered. When you create a composite control, you typically override a control's CreateChildControls() method. This method is called when a control builds its collection of child controls. For example, the control in Listing 31.8 combines a TextBox control and RequiredFieldValidator control. Listing 31.8. RequiredTextBox.vb
Notice that the control in Listing 31.8 inherits from the base CompositeControl class. Furthermore, rather than override the base control's RenderContents() method, the control overrides the base control's CreateChildControls() method. You should notice one other special thing in Listing 31.8. Notice that the EnsureChildControls() method is called in both the Get and Set methods of the Text property. The EnsureChildControls() method forces the CreateChildControls() method to be called. However, it prevents the CreateChildControls() method from being called more than once. The Text property gets or sets a property of a child control (the TextBox control). If you attempt to use the Text property before the CreateChildControls() method is called, then you receive a null reference exception. The child controls must be created before you can access any of the child control properties. The page in Listing 31.9 illustrates how you can use the RequiredTextBox control in a page. Listing 31.9. ShowRequiredTextBox.aspx
The page in Listing 31.9 has tracing enabled. If you look at the control tree for the page, you'll see that the RequiredTextBox control includes both a TextBox and RequiredFieldValidator control as child controls. Building Hybrid ControlsIn practice, you very rarely build pure composite controls. In most cases in which you override a control's CreateChildControls() method, you also override the control's RenderContents() method to specify the layout of the child controls. For example, the control in Listing 31.10 represents a Login control. In the control's CreateChildControls() method, two TextBox controls are added to the control's collection of child controls. Listing 31.10. Login.vb
In Listing 31.10, the RenderContents() method is overridden in order to layout the two TextBox controls. The TextBox controls are rendered within an HTML table (see Figure 31.5). Notice that each TextBox is rendered by calling the RenderControl() method. Figure 31.5. Performing layout with an HTML table.The default RenderContents() method simply calls the RenderControl() method for each child control. If you override the RenderContents() method, you have more control over the layout of the control. The Login control in Listing 31.10 uses an HTML table for layout. From a web standards perspective, using HTML tables for layout is frowned upon. The modified Login control in Listing 31.11 uses <div> tags instead of a <table> tag for layout. Listing 31.11. LoginStandards.vb
The control in Listing 31.11 works quite nicely in all recent browsers (Internet Explorer 6, Firefox 1, Opera 8) without requiring an HTML table for layout (see Figure 31.6). Figure 31.6. Performing CSS layout.Note Microsoft does not have the luxury of using <div> tags for layout. Because Microsoft must support very old browsers that have limited or no support for Cascading Style Sheets (HTML 3.2 browsers), the standard controls must rely on HTML tables for layout. |