Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Pro ASP.NET 2.0 In CSharp 2005 (2005) [eng]

.pdf
Скачиваний:
107
Добавлен:
16.08.2013
Размер:
29.8 Mб
Скачать

C H A P T E R 2 7

■ ■ ■

Custom Server Controls

Each type of custom control has its own advantages and disadvantages. In Chapter 14, you learned about user controls. User controls are easier to create than custom server controls, but server controls are far more powerful. Server controls beat user controls in two key areas:

Server controls give you complete control over the HTML you generate: In other words, you can create a control such as the ASP.NET Calendar, which provides a single object interface but renders itself as a complex combination of elements.

Server controls provide better design-time support: You can add them to the Toolbox in Visual Studio and set properties and add event handlers at design time. You can even configure the description that Visual Studio will show for each property, along with other design-time niceties.

All of ASP.NET’s web controls are server controls. In this chapter, you’ll learn how you can build your own.

CUSTOM CONTROL CHANGES IN .NET 2.0

Creating custom controls is a complex, detailed topic that’s well suited to a chapter of its own. ASP.NET 2.0 keeps the underlying control creation model from ASP.NET 1.x, but adds a number of important refinements. They include the following:

Control adapters: ASP.NET 1.x makes a distinction between up-level and down-level clients. However, it’s up to you to implement adaptive rendering by checking the properties of the current browser and writing conditional rendering code. In ASP.NET 2.0, a new model adds to the existing control model—adaptive rendering. Using this model, you can create an adapter that customizes a single control for a single type of browser, potentially allowing you to support different devices without rewriting your web forms.

Control state: In ASP.NET 1.x, controls stored all their information in view state, which could be disabled through the EnableViewState property. ASP.NET 2.0 adds control state, a protected section of view state that can’t ever be disabled.

Composite controls: These controls, which wrap a combination of existing server controls in a higher-level abstraction, were fairly easy to create in ASP.NET 1.x. In ASP.NET 2.0 they get even easier with a new CompositeControl base class.

Client callbacks: Rather than posting back the entire page, your custom control can send a request to the server to get just the additional information it needs. Client callbacks aren’t discussed in this chapter— instead, you’ll learn how to use them in Chapter 29.

The design-time support for controls also has a number of enhancements. You’ll learn about these in the next chapter.

899

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.

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

903

Figure 27-1. A bare-bones server control

Custom Controls in the Toolbox

To make it easier to use your custom control, you probably want to allow it to appear in the Toolbox. Impressively, Visual Studio 2005 has built-in Toolbox support for custom controls, provided you create them in a separate assembly.

To try this, add a Web Control project to your website solution by choosing File Add New Project. (Alternatively, you can add a class library project—the only difference is that you’ll need to add the assembly references and namespace imports needed for web development.

Note Remember, Visual Studio supports projectless development, which means it hides solution files away in a user-specific directory. This means that it’s fairly easy to lose the solution file (for example, by moving the website to another computer or renaming the website directory outside of Visual Studio). If you take this step, you won’t cause an error, but the next time you load your website, the custom control project won’t be appear in the design environment—instead, you’ll need to use Add Existing Project to get it back.

Once you’ve created your project, you can define your controls. You develop your control library project in the same way you work with any other DLL component. You can build the project at any time, but you can’t start it directly because it isn’t an actual application.

To test your controls, you need to use them in another application. You can use two approaches. First, you can add a reference in the same way that you add a reference to any other .NET assembly. Just right-click your website in the Solution Explorer, and choose Add Reference. Choose the Project tab, pick the custom control project you’ve created, and click OK. This copies the compiled control assembly to your Bin directory, making it available to your pages.

An easier approach is to use the new automatic Toolbox support in Visual Studio 2005. When you compile a project that contains custom server controls, Visual Studio examines each control and adds it dynamically to a temporary, project-specific section of the Toolbox at the top (see Figure 27-2). That means you can easily add controls to any page. When you drop the control on the page, Visual Studio automatically copies the assembly over to the Bin directory if you haven’t already created a reference, adds the Register directive if it’s not already present in the page, and then adds the control tag.

Tip As with any other type of reference in Visual Studio, every time you compile your project, the most recent version of the referenced assembly is copied into your web application’s Bin directory. This means that if you change and recompile a custom control after adding it to the Toolbox, you have no reason to remove and re-add it.

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.