Along with all the extenders listed in Table 1, the ACT also supplies a few traditional server controls with rich capabilities: the Accordion, Rating, ReorderList, and TabContainer controls.
The Accordion
control allows you to provide multiple collapsible panes and display
only one at a time. When the user clicks a new pane, the currently
displayed pane is collapsed to leave room for the new one.
The Rating
control provides an intuitive user interface to let users select the
number of stars that represents their rating of a given subject. The
control is the wrapped-up version of the user interface that several
Web sites provide to let users rate published items.
A data-bound control, ReorderList,
allows its child elements to be reordered on the client using
drag-and-drop functionality. To move an item in the list, the user
drags the item’s handle up to its new position. At the end of the
operation, the control posts back so that the new status of the data
source can be recorded.
Finally, the TabContainer control is a purely client-side container of tabbed forms.
You use any of these controls in the same way you would use any native ASP.NET server controls.
Improving the User Interface with Input Extenders
In
a perfect world where architects design applications in strict
accordance with requirements and keeping the user’s satisfaction in
mind, each logical data type features its own set of input controls. In
general, money, days of the week, ages, and quantities are all data
represented with numbers. However, each of these logical data types
might need a different user interface. The same can be said for URLs,
paths, IP addresses, and dates.
Both
in Windows and the Web, user interfaces are built by composing controls
together. The original toolbox of controls, though, is not particularly
rich in either Windows/WPF or ASP.NET. Input controls for Windows Forms
are still based on the original windows in Win32. Input controls for
Web pages are little more than wrappers for the HTML <input> element.
The
need for effective input controls is even higher today, now that AJAX
is taking root. The more work a page can do on the client, the better
the overall experience is for the user. The user experience is widely
improved because of saved postbacks but also because of the increased
interactivity, dynamic data formatting, and validation that ad hoc
input components can guarantee. As a result, input controls should be
more interactive than ever. ACT extenders can help a lot.
Motivation for Input Facilities
As mentioned, in HTML there’s one primary element for accepting data—the <input>
element. You use it for numbers, strings, dates, currency values, and
so forth. So what if you need numeric input to be restricted to a
specific range of values?
You
can leave the user free of typing any values and then enforce this
business rule through a server-side validation layer. Although
server-side validation shouldn’t be avoided (for the safety of data),
it would be even better if you could start yelling at wrong data as
it’s entered into the input box.
JavaScript
code is required to check values and ensure they comply with expected
types and formats. In some cases, a specialized user interface also is
required to make the user feel more comfortable. Here’s where some ad
hoc input extenders fit in. Let’s start with the Slider and NumericUpDown extenders. Both force users to enter only numbers that fall in a given range.
The Slider Extender
The Slider extender hides its associated TextBox and replaces it with a graphical slider moving through discrete steps within a minimum and maximum interval. The underlying TextBox
can always be set programmatically to any value you want. Note, though,
that the assigned value must be compatible with the numeric range set
via the slider; otherwise, the slider will silently fix it. Let’s
consider the following example:
<act:SliderExtender runat="server" ID="SliderExtender1"
TargetControlID="txtYourIncome"
Minimum="0"
Maximum="200000"
Steps="41"
BoundControlID="lblIncomeAsText" />
The slider applies to a TextBox control named txtYourIncome and ensures it is visually assigned a value in the range 0 through 200,000. The Steps property indicates how many discrete steps should be provided. The preceding setting lets the slider jump by 5000 every time.
Note
Why is Steps
is set to a weird 41 value, when a value of 40 would have probably
looked better? Correctly, 40 is what you get by dividing 200,000 by
5000. However, the property Steps
counts the number of steps ranging from 0 to 200000. You have to add
one more tick to be able to select 0 first and then values every 5000. |
If
you programmatically set the slider on the server, the value is first
converted to a number and then mapped to the nearest step value. If you
pass in a string (that is, not a number), the slider ignores it and
defaults to the initial value of the range. If you set the value of the
underlying text box on the client via JavaScript, the value is
correctly recognized over the next postback, but it is not immediately
reflected by the user interface. To programmatically change the value
in the slider from within the client, use the following code:
$find("SliderExtender1").set_Value(145678);
Why should you opt for $find instead of $get? As mentioned, the $get function is shorthand for document.getElementById and looks only for DOM elements. The $find function stands for Sys.Application.findComponent and applies to any component of the Microsoft AJAX Library that has been programmatically created.
The BoundControlID property refers to a DOM element that dynamically displays the current slider value. In most cases, you’ll use a <span> tag or a Label control for this purpose. Internally, the slider distinguishes between <input> and any other HTML elements. It sets the value property in the former case; otherwise, it uses the innerHTML property of the matching DOM element.
Tip
The slider script hides the underlying TextBox.
For this reason, it is recommended that you hide the text box
declaratively using CSS styles to avoid any flashing when the page
loads up. Finally, be aware that regular text boxes are displayed
inline, whereas slider boxes are positioned through blocks. |
Figure 1 shows a few input extenders in action in a sample page.
The NumericUpDown Extender
The NumericUpDown extender selects the next/previous value in a bound list of possible values. Despite what the name implies, NumericUpDown is not just a tool for specifying numeric quantities. The RefValues property allows you to list values to move through:
<act:NumericUpDownExtender ID="UpDown1" runat="server"
Width="200"
RefValues="None;1;2;3;4;5;More than 5;"
TargetControlID="Children" />
NumericUpDown
has the ability to retrieve reference values from a remote service. The
service is invoked asynchronously each time the user clicks the up or
down buttons. The service must be script-callable and include methods
with the following signature:
public int YourMethod(int current, string tag)
You configure the extender to use the remote service through the ServiceDownPath, ServiceDownMethod, ServiceUpPath, and ServiceUpMethod
properties. Finally, the up and down buttons don’t have to be
auto-generated via script. They can be buttons already defined in the
page and referenced through the TargetButtonDownID and TargetButtonUpID properties.
The FilteredTextBox Extender
Preventing
user mistakes involves controlling the input by filtering out undesired
characters, invalid expressions, or data of the wrong type. Using the
standard TextBox
control, you enforce proper input by validating it on the server, where
you can check whether the input string can be converted to a given data
type. Extenders simply save you from writing the JavaScript code
required to make checks on the client.
The FilteredTextBox
extender prevents a user from entering invalid characters into a text
box. It basically adds JavaScript that hooks up the keyboard activity
and filters out undesired keystrokes. You can configure the extender to
refuse certain characters or to ensure that only specified characters
are accepted. You use the FilterMode property for this setting: it accepts only ValidChars and InvalidChars as its value:
<act:FilteredTextBoxExtender ID="Filtered1" runat="server"
TargetControlID="YourAge"
FilterMode="ValidChars"
FilterType="Numbers" />
The FilterType
property determines the type of filter to apply—only numbers, only
lowercase or uppercase, and a custom set of characters. It should be
noted that a TextBox
with a watermark can’t be filtered to accept numeric values only. The
filter layer, in fact, automatically clears any watermark text you set.
The Calendar Extender
Using
the calendar extender, you make it virtually impossible for users to
type anything other than a date. ASP.NET comes with a server Calendar
control, but a calendar extender is really different, as it builds its
user interface entirely on the client, works entirely on the client,
and generates no postbacks at all as the user navigates to find the
month and day. Furthermore, if a given browser doesn’t support
JavaScript, the old text box is displayed.
<act:CalendarExtender ID="CalendarExtender1" runat="server"
TargetControlID="Birth"
Format="dd/MM/yyyy" />
The
preceding code snippet is sufficient to display a popup calendar as the
associated text box receives the focus. As an alternative, you can
display the popup when the user clicks a page button. The ID of the
button is set through the PopupButtonID property. The Format
property indicates the format of the date as it will be written to the
text box when the user dismisses the calendar popup. (See Figure 2.)
The Calendar extender is good for date-picking functionality; it is not as good for real calendaring functionality.
The MaskedEdit Extender
Would you really use the Calendar
extender to pick a date that represents a birth date? It is an option
and, all in all, it is one of the best options you have. But it is not
the perfect choice. Why is that so? A date that represents a birth date
would reasonably require you to navigate a few years back to find the
right day. A popup calendar is great to pick close dates; it loses part
of its appeal as it is employed to pick up just any date. To some
extent, we’re back to square one—using a text box to have users enter a
date. This raises a number of new issues—involving formatting, locales,
and separators.
When we start reasoning about data formatting and locales, Date
is not the only critical data type. Currency, numbers, special strings
such as URLs, disk paths, phone numbers, and e-mail addresses are all
great examples of data types for which a specialized and masked input
field is desirable. In ASP.NET, the MaskedEditTextBox control allows you to control input in a number of common scenarios. extender is a component that when attached to a
You can use the MaskedEdit
extender to enter numbers, dates, times, and date/times. The extender
decides its output based on given culture settings. The following code
snippet shows the typical way to use the extender with a text box that
accepts a date:
<asp:TextBox runat="server" ID="TextBox1" />
<act:MaskedEditExtender ID="MaskedEditExtender1" runat="server"
TargetControlID="TextBox1"
Mask="99/99/9999"
MaskType="Date" />
You define an input mainly through two properties: Mask and MaskType. The full list of properties is shown in Table 2.
Table 2. Properties of the MaskedEdit Extender
Property | Default | Description |
---|
AcceptAMPM | False | Boolean property, indicates whether an AM/PM symbol should be used when representing a time. |
AcceptNegative | None | Indicates whether a negative sign (-) is required for negative values. Feasible values come from the MaskedEditShowSymbol enumeration: None, Left, Right. |
AutoComplete | True | Boolean property, indicates whether empty mask characters not specified by the user must be automatically filled in. |
AutoCompleteValue | “” | Indicates the default character to use when AutoComplete is enabled. |
Century | 1900 | Indicates the century to use when a date mask has only two digits for the year. |
ClearMaskOnLostFocus | True | Boolean property, indicates whether to remove the mask when the TextBox loses the input focus. |
ClearTextOnInvalid | False | Boolean property, indicates whether to clear the TextBox when the user has entered invalid text. |
ClipboardEnabled | True | Boolean property, indicates whether to allow copy/paste with the clipboard. |
ClipboardText | “” | Indicates the prompt text to use when a clipboard paste is performed. |
CultureName | “” | Gets and sets the name of the culture to use. |
DisplayMoney | None | Indicates whether the currency symbol is displayed. Feasible values come from the MaskedEditShowSymbol enumeration: None, Left, Right. |
ErrorTooltipCssClass | “” | Gets and sets the CSS class for the ToolTip message. |
ErrorTooltipEnabled | False | Boolean property, indicates whether to show a ToolTip message when the mouse hovers over a TextBox with invalid content. |
Filtered | “” | Gets and sets the list of valid characters for the mask type when the “C” placeholder is specified |
InputDirection | LeftToRight | Indicates the text input direction. Feasible values come from the MaskedEditInputDirectionLeftToRight, RightToLeft. enumeration: |
Mask | “” | Specifies the mask of characters that is acceptable to the extender. |
MaskType | “” | Indicates the mask type using any of the values defined by the MaskedEditType enumeration. |
MessageValidatorTip | True | Boolean property, indicates whether a help message is displayed as the user types in the TextBox. |
OnBlurCssNegative | “” | Gets and sets the CSS class used when the TextBox loses the input focus and contains a negative value. |
OnFocusCssClass | “” | Gets and sets the CSS class used when the TextBox receives the input focus. |
OnFocusCssNegative | “” | Gets and sets the CSS class used when the TextBox gets the input focus and contains a negative value. |
OnInvalidCssClass | “” | Gets and sets the CSS class used when the text is not valid. |
PromptCharacter | _ | Gets and sets the prompt character being used for unspecified mask characters |
UserDateFormat | None | Indicates a particular date format. Feasible values are defined by the MaskedEditUserDateFormat enumeration. |
UserTimeFormat | None | Indicates a particular time format. Feasible values are defined by the MaskedEditUserTimeFormat enumeration. |
The MaskType property selects a general pattern from a predefined list—the MaskedEditType enumeration:
public enum MaskedEditType
{
None,
Date,
Number,
Time,
DateTime
}
By
selecting a mask type, you inform the extender that the target control
is going to accept a number, a date, a time, or both. The Mask
property (of string type) indicates the physical sequence of characters
that form a valid input for the text box. For example, “5/4/08” and “04-05-2008” are both valid dates, but they use different input masks.
To build a mask, you use a few predefined symbols as placeholders. The list of supported symbols is in Table 3.
For example, the “999,999.99” mask makes your code accept a number with
a decimal separator and, at most, a one thousand separator.
Table 3. Symbols Supported by the MaskedEdit Extender
Symbol | Description |
---|
9 | Indicates a numeric character |
L | Indicates a letter |
$ | Indicates a letter or a blank |
C | Indicates a custom case-sensitive character as defined by the Filtered property |
A | Indicates a letter or a custom character as defined by the Filtered property |
N | Indicates a numeric or custom character as defined by the Filtered property |
? | Indicates any character |
/ | Indicates a date separator according to the current culture |
: | Indicates a time separator according to the current culture |
. | Indicates a decimal separator according to the current culture |
, | Indicates a thousand separator according to the current culture |
\ | Indicates an escape character |
{ | Indicates the initial delimiter for repetition of masks |
} | Indicates the final delimiter for repetition of masks |
The appearance of the currency symbol is controlled by the DisplayMoney
property, and each character to type is represented by a prompt. The
default prompt is the underscore, but you can change it via the PromptCharacter property.
For dates, you can also use extra properties such as AcceptAMPM, Century, and even a custom user format in addition to the predefined formats defined by the MaskedEditUserDateFormat enumeration:
public enum MaskedEditUserDateFormat
{
None,
DayMonthYear,
DayYearMonth,
MonthDayYear,
MonthYearDay,
YearDayMonth,
YearMonthDay
}
Many of the settings that influence the formatting applied by the MaskedEdit extender descend from the current culture. The CultureName property indicates the culture to apply. Note that this setting overrides the culture setting defined for the page through the UICulture attribute in the @Page directive.
While
the masked extender provides dynamic formatting capabilities, an
additional component—the masked validator—ensures that any text typed
can be successfully parsed back to the expected type:
<act:MaskedEditValidator ID="MaskedEditValidator1" runat="server"
ControlExtender="MaskedEditExtender1"
ControlToValidate="TextBox1"
IsValidEmpty="False"
EmptyValueMessage="Number is required "
InvalidValueMessage="Number is invalid" />
The MaskedEditValidator control is a custom validator that you optionally attach to the MaskedEdit extender so that the content of the edited TextBox
is carefully verified. The validator ensures that the text matches the
mask. The validator performs server and client validation, and it can
be associated with a validation group, just like any other ASP.NET
validator control.
The Text property of a masked TextBox
returns formatted text. For a date, the property returns something like
“05/04/2008”; for a number input field, the property returns text like
“3,500.00”. The currency symbol is not included in the Text property, even though it is shown to the user in the page.
How can you parse the value returned by Text into the logical data type—be it a date or a decimal? You can use the static Parse method on the DateTime and Decimal
types, but you must pay due attention to the culture you use. For
example, “05/04/2008” can be either the 4th of May (US culture) or the
5th of April (European culture).
The
issue is that there’s no guaranteed matching between the culture used
by the input page and the server page. The risk is that users type the
date according to the European culture and have it processed on the
server as a US culture data. Worse yet, the 3500 value entered using,
say, Italian decimal and thousand separators in a numeric text box
(“3.500,00”) might throw an exception because the parser of the Decimal type defaults to the US culture where commas and the dot are reversed. You have to work around these issues programmatically.
The key fact to remember is that extenders default to the en-us culture unless the CultureName property is explicitly set. On the server, instead, the system defaults to the value of the UICulture property on the Page class. In your code-behind class, you first obtain a CultureInfo object that reflects the culture used for the user interface of the MaskedEdit extender. You can proceed as shown here:
string culture = "en-us";
if (!String.IsNullOrEmpty(MaskedEditExtender1.CultureName))
culture = MaskedEditExtender1.CultureName;
CultureInfo info = new CultureInfo(culture);
Next, you call the Parse method, specifying a format provider based on the selected culture:
NumberFormatInfo numberInfo = info.NumberFormat;
DateTimeFormatInfo dateInfo = info.DateTimeFormat;
DateTime when = DateTime.Parse(txtBirthDate.Text, dateInfo);
decimal amount = Decimal.Parse(txtAmount.Text, numberInfo);
Figure 3
shows the behavior of a page that uses the it-IT culture in the masked
editor but an en-US culture on the server. The preceding code snippet
performs the trick of transforming dates correctly.
The AutoComplete Extender
Auto-completion
consists of the program’s ability to predict the word the user is
typing from the first few characters he or she has just entered. In
Internet Explorer, for example, auto-completion keeps track of any text
you type in the address bar and form fields and offers suggestions
whenever you’re typing again in a similar control. This feature is
entirely browser-led and can just be turned on and off for <input> and <form> tags by setting the autocomplete attribute to off. Note, though, that the autocomplete attribute is not a standard HTML attribute, although today nearly all browsers do recognize and support it.
The AutoComplete extender in the ACT provides a similar behavior for TextBox
controls, but it makes the developer responsible for all the logic that
provides possible words to the user. The extender creates a drop-down
panel, much like a drop-down list, and positions it right at the bottom
of the text box. Here’s how to associate an auto-complete extender with
a text box:
<act:AutoCompleteExtender runat="server" ID="AutoComplete1"
TargetControlID="TextBox1"
MinimumPrefixLength="3"
ServicePath="Suggestions.asmx"
ServiceMethod="GetSuggestions" />
The extender is bound to a Web service or WCF service that actually provides the words to populate the drop-down list. The MinimumPrefixLength
property instructs the control when to place a call to the Web
service—for example, only when the user has typed at least the
specified number of characters. The text already typed in will be used
as input for the specified Web service method. The response is used to
populate the drop-down list.
The EnableCaching
Boolean property can also be turned on. If you do so, typing the same
prefix more than once results in a single call to the Web service.
Furthermore, depending on the way the Web service retrieves its data,
you also can enable caching on the server to save some extra roundtrips
to a database or another remote data store. Table 4 shows the full list of properties supported by the extender.
Table 4. Properties of the AutoComplete Extender
Property | Description |
---|
Animations | Sets animations to be played when the flyout is shown and hidden. |
CompletionInterval | Gets and sets the number of milliseconds after which the extender gets suggestions using the bound Web service. |
CompletionListCssClass | Indicates the CSS class used to style the completion list flyout. |
CompletionListHighlightedItemCssClass | Indicates the CSS class used to style the highlighted item in the completion list flyout. |
CompletionListItemCssClass | Indicates the CSS class used to style the item in the completion list flyout. |
CompletionSetCount | Gets and sets the number of suggestions to get from the bound Web service. Default is 10. |
ContextKey | String property, indicates any page or user-specific information to pass to the bound Web service. |
DelimiterCharacters | Indicates
one or more characters that the extender will use to tokenize the
text-box content. The Web service will then use the last of these token
to provide suggestions. Not set by default. |
EnableCaching | Boolean property, indicates whether client-side caching is enabled. True by default. |
FirstRowSelected | Boolean property, indicates whether the first option in the list will be automatically selected. False by default. |
MinimumPrefixLength | Gets and sets the minimum number of characters in the text-box buffer that trigger the bound Web service. Default is 3. |
ServiceMethod | Gets and sets the name of the method to invoke on the bound Web service. |
ServicePath | Gets and sets the URL of the bound Web service. |
UseContextKey | Boolean property, indicates whether the value of the ContextKeyFalse by default. property should be used, if specified. |
A Web service that works with the AutoComplete
extender is an ASP.NET AJAX script service. It looks nearly the same as
a regular ASP.NET Web service, except that its class must be decorated
with the ScriptService
attribute. If you employ a Web service that lacks the attribute, each
request to the associated ASMX endpoint originates an HTTP 500 error.
[ScriptService]
public class SuggestionService : WebService
{
:
}
The name of any public method on the class that is flagged with the WebMethod attribute can be successfully assigned to the ServiceMethod property of the extender. A method that provides suggestions must have the following signature:
[WebMethod]
public string[] GetCustomerIDs(string prefixText, int count)
The
first argument is the prefix text to generate suggestions. It matches
the current content of the text box and its length is not smaller than
the value of the MinimumPrefixLength property. The count parameter indicates how many suggestions are to be provided. The value of the count parameter comes from the value of the CompletionSetCount property. The return value is always packed as an array of strings. Because of the ScriptService attribute, any communication between the server and client occurs through JSON strings.
You can leverage any supported attributes on Web service methods that will make the call go faster. For example, the CacheDuration attribute on the WebMethod
attribute forces the service to cache internally for the specified
duration the response of the method call. Likewise, you can enable
session state if it’s strictly required by the logic the method
implements. See Figure 4.