ASP.NET comes with a fairly rich collection of
built-in controls. In addition, plenty of custom controls are available
for developers from third-party vendors, from community projects, and
even from contributions by volunteers. If you still can’t find the
control you are looking for,
you typically write one yourself or buy a new specialized control that
extends the original control and adds the desired behavior. Object
orientation, of course, encourages this approach.
All
in all, it’s rare that you need to write a completely new control
yourself. More often, your control will derive from an existing ASP.NET
control. Blindly using inheritance for building specialized versions of
controls might not be a wise choice, though. Even in relatively small
projects, in fact, it can lead straight to a proliferation of controls.
ASP.NET
control extenders go in the opposite direction. First and foremost, an
extender control is a server control itself. An extender represents a
logical behavior that can be attached to one or more control types to
extend their base capabilities. Extenders decouple controls from
behaviors and make it possible to extend existing controls with new
behaviors.
From
a technology point of view, ASP.NET AJAX and extenders are not strictly
related. In theory, one could develop extenders for ASP.NET 1.1 and
ASP.NET 2.0 that work without AJAX extensions. In practice, though, ACT
provides some interesting facilities for writers of extender
controls—specifically, base classes and, more importantly, the
Microsoft AJAX library for developing JavaScript functionalities more
comfortably.
Extenders at a Glance: the TextBoxWatermark Control
To
better understand the goals and characteristics of control extenders,
let’s briefly consider the behavior encapsulated by one of the
extenders contained in the ACT—the TextBoxWatermark extender.
A
text box watermark is a string of text that is displayed in an empty
text box as a guide to the user. This help text is stripped off when
the text box is submitted and is automatically removed as the user
starts typing in the field. Likewise, it is automatically re-inserted
when the user wipes out any text in the text box. You start by linking
the ACT assembly to the project and then place an extendee TextBox control in the page:
<%@ Register Assembly="AjaxControlToolkit" Namespace="AjaxControlToolkit" TagPrefix="act" %>
...
<asp:TextBox ID="TextBox1" runat="server" />
Later in the ASPX source, you add a new control—the TextBoxWatermarkExtender control:
<act:TextBoxWatermarkExtender runat="server" ID="TextBoxWatermark1"
TargetControlID="TextBox1"
WatermarkText="Type First Name Here"
WatermarkCssClass="watermarked" />
The
watermark extender targets the specified control ID and adds a new
behavior to it. The behavior is further configured using a few public
properties on the extender control, such as WatermarkText and WatermarkCssClass in the previous example.
In particular, the watermark behavior injects script code that hooks up three HTML events: onfocus, onblur, and onkeypress.
In its initialization stage, the injected script also sets a new style
and default text for the target text box if the body of the field is
empty. When the text box gets the input focus, the event handler
promptly removes the watermark text and restores the original style. As
the user types, the handler for onkeypress ensures that the current text box is watermarked. Finally, when the input field loses the focus—the onblur event—the handler sets the watermark back if the content of the field is the empty string.
Note
To add a watermark behavior to an ASP.NET TextBox,
you use the aforementioned extender control. Alternatively, if you feel
comfortable with ASP.NET control development and JavaScript, you can
develop a custom TextBox control and use a client-side code fragment to achieve the same results. |
Creating a New Extender Control
ASP.NET
3.5 doesn’t include any concrete implementation of an extender.
However, it defines the base class from which all custom extenders, as
well as all extenders in the ACT, derive. This class is named ExtenderControl.
You can create your own extenders starting from this class; it is not
recommended, though. Why is it so? There’s an easier and faster way
that leverages the extensions available in the ACT library.
The
following code shows the source code of the focus extender control. The
sample extender adds to its target control a highlighting behavior that
changes the appearance of the control when this gets focused:
using AjaxControlToolkit;
...
namespace Core35
{
[TargetControlType(typeof(Control))]
[ClientScriptResource("Core35.FocusBehavior", "focusBehavior.js")]
public class FocusExtender : AjaxControlToolkit.ExtenderControlBase
{
[ExtenderControlProperty]
[RequiredProperty]
public string HighlightCssClass
{
get { return GetPropertyValue("HighlightCssClass", ""); }
set { SetPropertyValue("HighlightCssClass", value); }
}
[ExtenderControlProperty]
public string NoHighlightCssClass
{
get { return GetPropertyValue("NoHighlightCssClass", ""); }
set { SetPropertyValue("NoHighlightCssClass", value); }
}
}
}
The TargetControlType attribute indicates the type of controls this behavior can be attached to. The ClientScriptResource attribute indicates the name of the script class to inject in the client page and its source file. The base class is ExtenderControlBase, which is defined in the ACT library.
All
that you do with managed code is define the set of properties that
developers can customize on the both the server and the client. Each
property must be decorated with the ExtenderControlProperty attribute and, optionally, the RequiredProperty attribute.
The
property is not directly responsible for the persistence of its
assigned value. It is limited to getting and setting the value through
the GetPropertyValue and SetPropertyValue methods of the base class. These stock methods take care of persistence.
The core part of an AJAX extender control is its JavaScript code. Here’s the JavaScript code you need for the focus extender:
Type.registerNamespace('Core35');
Core35.FocusBehavior = function(element)
{
Core35.FocusBehavior.initializeBase(this, [element]);
this._highlightCssClass = null;
this._nohighlightCssClass = null;
}
Core35.FocusBehavior.prototype =
{
initialize : function() {
Core35.FocusBehavior.callBaseMethod(this, 'initialize');
this._onfocusHandler = Function.createDelegate(this, this._onFocus);
this._onblurHandler = Function.createDelegate(this, this._onBlur);
$addHandlers(this.get_element(),
{ 'focus' : this._onFocus,
'blur' : this._onBlur },
this);
this.get_element().className = this._nohighlightCssClass;
},
dispose : function() {
$clearHandlers(this.get_element());
Core35.FocusBehavior.callBaseMethod(this, 'dispose');
},
_onFocus : function(e) {
if (this.get_element() && !this.get_element().disabled) {
this.get_element().className = this._highlightCssClass;
}
},
_onBlur : function(e) {
if (this.get_element() && !this.get_element().disabled) {
this.get_element().className = this._nohighlightCssClass;
}
},
get_highlightCssClass : function() {
return this._highlightCssClass;
},
set_highlightCssClass : function(value) {
if (this._highlightCssClass !== value) {
this._highlightCssClass = value;
this.raisePropertyChanged('highlightCssClass');
}
},
get_nohighlightCssClass : function() {
return this._nohighlightCssClass;
},
set_nohighlightCssClass : function(value) {
if (this._nohighlightCssClass !== value) {
this._nohighlightCssClass = value;
this.raisePropertyChanged('nohighlightCssClass');
}
}
}
// Optional descriptor for JSON serialization
Core35.FocusBehavior.descriptor = {
properties: [ {name: 'highlightCssClass', type: String},
{name: 'nohighlightCssClass', type: String} ]
}
// Register the class as a type that inherits from Sys.UI.Control.
Core35.FocusBehavior.registerClass('Core35.FocusBehavior', Sys.UI.Behavior);
The script for the extender is centered around a couple of handlers for focus and blur DOM events. In the focus handler, the code sets the CSS class for the target element. In the blur handler, it resets the CSS class.
Tip
The
easiest way to create extenders is to use the facilities of the ACT.
You run the VSI file in the toolkit download, set up the templates, and
then click “Add AJAX Control Extender” from the Visual Studio 2008 Solution Explorer to obtain a scaffold for the extender and the behavior. |
In
a test page, all that you have to do is register the ACT assembly and
the assembly that contains the focus extender and then go with the
following code:
<asp:TextBox ID="TextBox1" runat="server" EnableTheming="false" />
<act:FocusExtender ID="FocusExtender1" runat="server"
TargetControlID="TextBox1"
NoHighlightCssClass="LowLightTextBox"
HighlightCssClass="HighLight" />
You
can attach the focus extender behavior to virtually all ASP.NET
controls and, for each control, you can specify the CSS class to use to
render the control as highlighted and normal. Bear in mind that you
need to disable theming to make sure that CSS styles you apply through
the extender take precedence over any other style set via themes. Figure 1 shows the extender in action.
Extenders in the ACT
Table 1
lists the components available in the ACT. Note that the full name of
the extender class contains an “Extender” suffix that I omitted in the
table for brevity. So, for example, there will be no CollapsiblePanel component in the ACT assembly; you will find a CollapsiblePanelExtender control instead.
Table 1. Extenders in the ACT
Control | Description |
---|
AlwaysVisibleControl | Pins
a control to a corner of the page, and keeps it floating over the page
background as the user scrolls or resizes the page. You use this
extender to make sure that, say, a given panel shows up at the top-left
corner of the page regardless of the scroll position or the size of the
browser window. |
Animation | Provides
a specialized framework for adding animation effects to controls hosted
in ASP.NET pages. You associate client-side events of the target
control with one or more of the predefined animation effects. |
AutoComplete | Associated with a text box, provides a list of suggestions for the text to type in the field. |
Calendar | Attached
to a text box, the extender provides client-side date-picking
functionality with customizable date format and pop-up control. |
CascadingDropDown | Associated with a DropDownList
control. This extender automatically populates the list with data
retrieved from a Web service method. The nice thing about this extender
is that you can create a hierarchy of drop-down lists and have the
extender automatically populate child drop-down lists based on the
current selections in any of the previous lists in the hierarchy, if
any. |
CollapsiblePanel | Adds collapsible sections to a Web page. This extender can be used only with panel controls—that is, with the ASP.NET Panel
control or any class derived from it. You let the extender know which
panel in the page acts as the header and which panel provides the
contents that collapse and expand. |
ConfirmButton | Associated
with a button control. This extender adds a confirmation JavaScript
dialog box to the click event of the button. The extender is supported
on any class that implements the IButtonControl interface, including Button, LinkButton, and ImageButton. |
DragPanel | Associated
with panel controls. This extender adds drag-and-drop capabilities so
that you can move the panel around the page. You can specify the
contents to move as well as the handle that, if pressed, triggers the
dragging operation. |
DropDown | The extender provides a mouse-over link to open a drop-down panel. |
DropShadow | Adds drop shadows to any control available on the page. With this extender, you can specify the opacity and width of the shadow. |
DynamicPopulate | Updates the contents of a control with the result of a Web service or page method call. |
FilteredTextBox | Lets users enter text in a TextBox control that matches a given set of valid characters. |
HoverMenu | Displays
the contents of an associated panel control when the mouse hovers next
to a given control. You can associate this extender with any ASP.NET
control. The extender works as a kind of specialized and extremely
flexible ToolTip. |
ListSearch | Enables users to search for an item in a list by typing some of the characters. |
MaskedEdit | Lets users enter text in a TextBox control according to a given input layout. |
ModalPopup | Associated with a control that can fire a client-side onclick
event (typically, buttons and hyperlinks), this extender implements a
classic modal dialog box without using HTML dialog boxes. Basically, it
displays the contents of a given panel and prevents the user from
interacting with the rest of the page. |
MutuallyExclusiveCheckBox | Associated with CheckBox controls, this extender lets you define logical groups of check boxes so that users can select only one in each group. |
NoBot | Applies some anti-bot
techniques to input forms. Bots, or robot applications, are software
applications that run automated tasks over the Internet. For example,
bots are used to fill input forms and submit ad hoc values. |
NumericUpDown | Associated
with text box controls, this extender allows you to click automatically
displayed buttons to enter the next/previous value in the field. It
works with numbers, custom lists, and Web service methods. |
PagingBulletedList | Associated with BulletedList controls, this extender groups all items bound to the list and organizes them in client-side sorted pages. |
PasswordStrength | Associated
with text box controls used to type a password, this extender provides
visual feedback on the strength of the password being typed. |
PopupControl | Transforms
the contents of a given panel into a pop-up window without using HTML
dialog boxes. You can associate this extender with any control that can
fire any of the following client-side events: onfocus, onclick, and onkeydown. |
ResizableControl | Attaches
to any page element, and allows the user to resize the element using a
handle placed at the lower-right corner of the control. |
RoundedCorners | Adds
a background panel to any ASP.NET control so that the control appears
with rounded corners. The overall height of the original control
changes slightly. |
Slider | Extends a TextBox control with a slider user interface. |
SlideShow | Associated with Image controls, it can be used to transition images automatically when hosted on a page. |
TextBoxWatermark | Associated with TextBox
controls. This extender adds sample or prompt text, called a
“watermark,” that illustrates the type of text the user is expected to
enter in the field. For example, the watermark might say, “Type your
name here.” The watermark text disappears as soon as the user starts
typing and reappears if the text box becomes empty. |
ToggleButton | Associated with CheckBox
controls. This extender enables you to use custom images to render the
check buttons. You can use different images to indicate the selected
and cleared states. |
UpdatePanelAnimation | Plays
animations during key steps of a partial update. You can use the
extender to animate the page both while the panel is being updated and
when the update has completed. |
ValidatorCallout | Works
on top of ASP.NET validators, and improves their user interface. In
particular, the extender displays a yellow callout with the error
message. |
As
you can probably guess, some extenders listed in the table require rich
browser capabilities, whereas others are just a smart piece of
JavaScript code attached to a block of markup elements. Note that all
these features work in a cross-browser way. I’ll return to some of the
aforementioned extenders with code samples and more details in a
moment. For more complete documentation, please refer to http://www.asp.net/ajax/ajaxcontroltoolkit/samples.