Pro ASP.NET 2.0 In CSharp 2005 (2005) [eng]
.pdf
900 C H A P T E R 2 7 ■ C U S TO M S E R V E R C O N T R O L S
Custom Server Control Basics
Server controls are .NET classes that derive from System.Web.UI.WebControls.WebControl (which itself derives from Control) or System.Web.UI.Control. The Control class provides properties and methods that are common across all server controls (such as ID, ViewState, and the Controls collection). The WebControl class adds a few features that help you implement standard styles. These include properties such as Font, ForeColor, and BackColor.
Ideally, you’ll create your server controls in a separate class library project and compile the project into a separate DLL assembly. Although you can create a custom control and place the source code directly in the App_Code directory, this limits your ability to reuse the control in pages written in different languages. If you place controls in a separate assembly, you’ll also have better design-time support when you use them in pages.
To get a better idea of how custom controls work, the following sections demonstrate a couple of simple examples.
Creating a Bare-Bones Custom Control
To create a basic custom control, you need to derive from the Control class and override the Render() method. The Render() method provides an HtmlTextWriter object that you use to generate the HTML for the control. The simplest approach is to call HtmlTextWriter.Write() to write a string of raw HTML to the page. (ASP.NET tags and other server-side content obviously won’t work here.)
Here’s an example control that generates a simple hyperlink using the HtmlTextWriter in the Render() method:
public class LinkControl : Control
{
protected override void Render(HtmlTextWriter output)
{
output.Write(
"<a href='http://www.apress.com'>Click to visit Apress</a>");
}
}
The HtmlTextWriter class not only lets you write raw HTML but it also provides some helpful methods to help you manage style attributes and tags. Table 27-1 describes the key methods.
Table 27-1. HtmlTextWriter Methods
Method |
Description |
AddAttribute() |
Adds any HTML attribute and its value to an HtmlTextWriter output stream. |
|
This attribute is automatically used for the next tag you create by calling |
|
RenderBeginTag(). Instead of using the exact attribute name, you can |
|
choose a value from the HtmlTextWriterAttribute enumeration. |
AddStyleAttribute() |
Adds an HTML style attribute and its value to an HtmlTextWriter output |
|
stream. This attribute is automatically used for the next tag you create by |
|
calling RenderBeginTag(). Instead of using the exact style name, you can |
|
choose a value from the HtmlTextWriterStyle enumeration, and it will be |
|
rendered appropriately depending on whether the browser is an up-level |
|
or down-level client. |
RenderBeginTag() |
Writes the start tag for the HTML element. For example, if you are writing |
|
an anchor tag, this writes <a>. Instead of using the exact tag name, you can |
|
choose a value from the HtmlTextWriterTag enumeration. |
C H A P T E R 2 7 ■ C U S TO M S E R V E R C O N T R O L S |
901 |
Method |
Description |
RenderEndTag() |
Writes the end tag for the HTML element. For example, if you are in the |
|
process of writing an anchor tag, this writes the closing </a>. You don’t |
|
need to specify the tag name. |
WriteBeginTag() |
This method is similar to the RenderBeginTag() method, except it doesn’t |
|
write the closing > character for the start tag. That means you can add call |
|
WriteAttribute() to add more attributes to the tag. |
WriteAttribute() |
Writes an HTML attribute to the output stream. This must follow the |
|
WriteBeginTag() method. |
WriteEndTag() |
Writes the closing > character for the current HTML tag (the one that was |
|
last opened using the WriteBeginTag() method). |
|
|
Using the HtmlTextWriter methods, you can modify the rendering code. The next example presents the same control, with a couple of minor differences. First, it renders the start tag and the end tag for the anchor separately, using the RenderBeginTag() and RenderEndTag() methods. Second, it adds style attributes that configure how the control will appear. Here’s the complete code:
public class LinkControl : Control
{
protected override void Render(HtmlTextWriter output)
{
//Specify the URL for the upcoming anchor tag. output.AddAttribute(HtmlTextWriterAttribute.Href,
"http://www.apress.com");
//Add the style attributes. output.AddStyleAttribute(HtmlTextWriterStyle.FontSize, "20"); output.AddStyleAttribute(HtmlTextWriterStyle.Color, "Blue");
//Create the anchor tag. output.RenderBeginTag(HtmlTextWriterTag.A);
//Write the text inside the tag.
output.Write("Click to visit Apress");
// Close the tag. output.RenderEndTag();
}
}
You should note a few important points in this example. First, to make life easier, the example uses several enumerations. These enumerations help avoid minor typographic mistakes that would cause unexpected problems. The enumerations include the following:
•HtmlTextWriterTag: This enumeration defines a large set of common HTML tag attributes such as onClick, href, align, alt, and more.
•HtmlTextWriterAttribute: This enumeration defines dozens of HTML tags, such as <a>, <p>, <font>, and many more.
•HtmlTextWriterStyle: This enumeration defines 14 style attributes, including BackgroundColor, BackgroundImage, BorderColor, BorderStyle, BorderWidth, Color, FontFamily, FontSize, FontStyle, FontWeight, Height, and Width. All these pieces of information are joined in a semicolon-delimited list, which is sets the style attribute.
902 C H A P T E R 2 7 ■ C U S TO M S E R V E R C O N T R O L S
When the Render() method executes, it begins by defining all the attributes that will be added to the upcoming tag. Then when the start tag is created (using the RenderBeginTag() method), all of these attributes are placed into the tag. The final rendered tag looks like this:
<a href="http://www.apress.com" style="font-size:20;color:Blue;"> Click to visit Apress</a>
Using a Custom Control
To use a custom control, you need to make it available to your web application. You have two choices—you can copy the source code to the App_Code directory, or you can add the compiled assembly to the Bin directory (using Visual Studio’s Add Reference command).
For the page to have access to a custom control, you must use the Register directive, just as you did with user controls in Chapter 14. However, this time you need to indicate slightly different information. Not only must you include a TagPrefix but you also need to specify the assembly file (without the DLL extension) and the namespace where the control class is located. You don’t need to specify the TagName, because the server control’s class name is used automatically.
Here’s an example of the Register directive:
<%@ Register TagPrefix="apress" Namespace="CustomServerControlsLibrary" Assembly="CustomServerControlsLibrary" %>
You can reuse tag prefixes. In other words, it’s completely valid to map two different namespaces or two completely different assemblies to the same tag prefix.
■Tip If the control is in the App_Code directory of the current web application, you don’t need to include the Assembly attribute.
If you want to use a control in several pages of the same web application, ASP.NET 2.0 adds a helpful shortcut—you can register the tag prefix in the web.config file like this:
<configuration xmlns="http://schemas.microsoft.com/.NetConfiguration/v2.0"> <system.web>
<pages>
<controls>
<add tagPrefix="apress" namespace="CustomServerControlsLibrary" assembly="CustomServerControlsLibrary" />
</controls>
</pages>
...
</system.web>
</configuration>
This is particularly handy if you want to standardize on a specific tag prefix. Otherwise, Visual Studio chooses a default prefix (such as cc1 for custom control 1) when you drop a control from the Toolbox.
Once you’ve registered the control, you can declare it with a standard control tag, as shown here:
<apress:LinkControl id="LinkControl1" runat="server"/>
Figure 27-1 shows the custom LinkControl in action.
904 C H A P T E R 2 7 ■ C U S TO M S E R V E R C O N T R O L S
Figure 27-2. A custom control in the Toolbox
The only limitation of the automatic Toolbox support is that your custom controls will appear in the Toolbox only when the custom control project is loaded in the design environment. If you want to make a control available to any web application but you don’t want the web application developers to be able to change your custom control code, you need another approach. In this case, it makes sense to deploy just the compiled assembly. You can then add the controls to the Toolbox permanently so the application developers don’t need to worry about finding the control.
To do this, right-click the Toolbox, and select Choose Items. Next, click the .NET Framework Components tab, and then click the Browse button. Then choose the custom control assembly from the file browser. The controls will be added to the list of available .NET controls, as shown in Figure 27-3.
All checked controls will appear in the Toolbox. Note that controls aren’t added on a perproject basis. Instead, they will remain in the Toolbox until you delete them. To remove a control, right-click it, and select Delete. This action removes the icon only, not the referenced assembly.
Visual Studio gives you quite a bit of basic design-time support. For example, after you add a custom control to a web page, you can modify its properties in the Properties window (they will appear under the Misc group) and attach event handlers. In Chapter 28, you’ll learn how you can further customize the design-time behavior and appearance of your control.
Creating a Web Control That Supports Style Properties
The previous custom control example doesn’t allow the web page to customize the control’s appearance. The custom control doesn’t provide any properties for setting foreground or background colors, the font, or other attributes of the HTML tag that you generate. To add support for these features, you need to explicitly add public properties that represent these values. You then need to read these properties in the Render() method and generate the appropriate HTML code.
Of course, style properties are a basic part of infrastructure that many HTML controls need to use. Ideally, all controls should follow a single, streamlined model for style information and not force custom control developers to write this generic functionality themselves. ASP.NET does this with the WebControl base class (in the System.Web.UI.WebControls namespace). Every web control that’s included with ASP.NET derives from WebControl, and you can derive your custom controls from it as well.
C H A P T E R 2 7 ■ C U S TO M S E R V E R C O N T R O L S |
905 |
Figure 27-3. Adding a custom control to the Toolbox
Not only does the WebControl class include basic style-related properties such as Font, ForeColor, BackColor, and so on, but it also renders them automatically in the control tag. Here’s how it works: the WebControl assumes that it should add the attributes to a single HTML tag, called the base tag. If you’re writing multiple elements, the attributes are added to the outermost element that contains the other elements. You specify the base tag for your web control in the constructor.
Finally, you don’t override the Render() method. The WebControl already includes an implementation of Render() that farms the work out to the following three methods:
•RenderBeginTag(): This method is called to write the opening tag for your control, along with the attributes you’ve specified.
•RenderContents(): This method writes everything between the start and end tag, which can include text content or other HTML tags. This is the method you’ll override most often to write your custom control content.
•RenderEndTag(): This method is called to write the closing tag for your control.
Of course, you can change this behavior by overriding the Render() method, if needed. But if this basic framework suits your needs, you’ll be able to accomplish quite a bit with little custom code.
The next example demonstrates a new link control that derives from WebControl and thereby gains automatic support for style properties.
public class LinkWebControl : WebControl { ... }
The default constructor calls the WebControl constructor. More than one version of WebControl constructor exists—this code uses the version that allows you to specify a base control tag. In this example, the base control tag is the <a> anchor, as shown here:
public LinkWebControl() : base(HtmlTextWriterTag.A) {}
906 C H A P T E R 2 7 ■ C U S TO M S E R V E R C O N T R O L S
The LinkWebControl constructor doesn’t require any actual code. It’s just important that you use this opportunity to call the WebControl constructor to set the base control tag. If you use the default (zero-parameter) WebControl constructor, a <span> tag is used automatically. You can then render additional HTML inside this <span> tag, which ensures that all elements will have the same style attributes.
The LinkWebControl also defines two properties, which allow the web page to set the text and the target URL:
private string text; public string Text
{
get {return text;} set {text = value;}
}
private string hyperLink; public string HyperLink
{
get {return hyperLink;} set
{
if (value.IndexOf("http://") == -1)
{
throw new ApplicationException("Specify HTTP as the protocol.");
}
else
{
hyperLink = value;
}
}
}
You could set the text and hyperLink variables to empty strings when you define them. However, this example overrides the OnInit() method to demonstrate how you can initialize a control programmatically:
protected override void OnInit(EventArgs e)
{
base.OnInit(e);
if (hyperLink == null)
hyperLink = "http://www.apress.com";
if (text == null)
text = "Click here to visit Apress";
}
The LinkWebControl presents a minor challenge. To successfully create an <a> tag, you need to specify a target URL and some text. The text is placed between the start and end tags. However, the URL is added as an attribute (named href) to the start tag. As you’ve already learned, the WebControl manages the attributes for the start tag automatically. Fortunately, the WebControl class gives you the ability to add extra tags by overriding the method AddAttributesToRender(), as shown here:
protected override void AddAttributesToRender(HtmlTextWriter output)
{
output.AddAttribute(HtmlTextWriterAttribute.Href, HyperLink); base.AddAttributesToRender(output);
}
C H A P T E R 2 7 ■ C U S TO M S E R V E R C O N T R O L S |
907 |
Note that whenever a custom control overrides a method, it should call the base class implementation using the base keyword. This ensures that you don’t inadvertently suppress any code that needs to run. Often, all the base method does is fire a related event, but that’s not always the case. For example, if you override RenderBeginTag() and don’t call the base implementation, the rendering code will fail with an unhandled exception because the tag isn’t opened.
Finally, the RenderContents() method adds the text inside the anchor:
protected override void RenderContents(HtmlTextWriter output)
{
output.Write(Text);
base.RenderContents(output);
}
Note that the code doesn’t use the style properties. Instead, ASP.NET applies these automatically when it renders the base tag.
Now that you have created the control, you can use it in any ASP.NET web page. You can set the style properties in code or in the control tag. You can even use the Properties window. Here’s an example:
<apress:LinkWebControl id="LinkWebControl1" runat="server" BackColor="#FFFF80" Font-Names="Verdana" Font-Size="Large" ForeColor="#C00000" Text="Click to visit Apress" HyperLink="http://www.apress.com"></apress:LinkWebControl>
The HyperLink and Text attributes are automatically mapped to the corresponding public properties of the custom control. The same is true of the style-related properties, which are defined in the base WebControl class.
Figure 27-4 shows this control in a web browser.
Figure 27-4. A custom control that supports style properties
■Tip As a general guideline, you should derive from the WebControl class if your control needs to create any type of user interface. Of course, exceptions exist. For example, if you know you want only a subset of the UI features, or you want to combine multiple controls, which will each have their own specific style properties, you might want to derive from Control instead of WebControl. However, the basic rule of thumb that the .NET class library follows is always to derive from WebControl, even if some of the properties aren’t relevant.
