Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

.NET

ASP.NET Server Components


Jun02: ASP.NET Server Components

Doug is the author of Designing Microsoft ASP.NET Applications and owner of Access Microsystems. Doug can be reached at [email protected].


Active Server Pages (ASP) have been a powerful tool for building Microsoft-centric scalable, dynamic web applications. Of course, there are things ASP does not do well (or at all), forcing you move to tools such as Visual Basic. You can create COM components with Visual Basic that do some of the things ASP can't do, including low-level access to the Win32 API and e-mail. Alas, these COM components present a deployment challenge and do little to enhance client-side interactions of application users.

With the ASP.NET, on the other hand, you can create components in the same languages as the web form logic, notably VB.NET and C#. Since these components are not COM components, many COM overhead and deployment issues disappear. One significant improvement that ASP.NET has over ASP is support within the ASP.NET framework for components, which lets you split the task of the component between the client and the server.

The ASP.NET Component Model

Of course, ASP applications could use client-side scripting — a time-consuming, detailed process. To use client-side code, you need to write the application so that it emits JavaScript code. Controlling what code is emitted and ensuring that only a single copy of it is written out is not difficult, but error prone. Consequently, most ASP developers ignore the client side's computing power, opting for server-side code.

ASP.NET provides an alternative model for server-side controls. Server-side controls are commonly (though not exclusively) wrappers that provide added functionality to existing HTML widgets. For instance, the TextBox control lets you forget about all the details of the Text Input HTML control, and instead set properties of the control. Rather than setting a Value attribute within the <INPUT> tag, you can either use the designer or (in code) say TextBox1.Text="Hi There!". Through ASP.NET, you can even retrieve the value of the TextBox using, for example, strRet=TextBox.Text.

In addition to normal server-side components, ASP.NET lets you create components that automatically sense whether the client can support client-side scripting, then emit code that loads the client-side code (generally JavaScript). While the net effect is the same as what could be achieved using ASP, creating a component one time to generate client-side script is much easier than remembering to use it on individual pages.

Of course, most of the reasons for using client-side scripting on web pages are to do client-side validation. There are still some sorts of specialized client-side validation that might be best suited for custom JavaScript, but before using it you should consider validation in ASP.NET.

Validation in ASP.NET

In addition to controls that wrap HTML controls, ASP.NET provides a group of controls called "validators." Instead of being manipulated directly by users, validators are hooked to another control and the information entered into the control is then validated. By default, validation is done on the client and server. Client-side-only validation is a bad idea, since it is possible that a client machine may not have a functional JavaScript interpreter, even though it seems that it does.

Table 1 lists the validator types supported by ASP.NET. (For details on all possible validators, see the MSDN documentation, especially about the ValidationSummary control.) The ValidationSummary control is completely different from the CompareValidator in Listing One. This code generates a simple page, performing a common task. The first field is for the current password, and the last two fields are for entering and confirming a new password. If you have ASP.NET on your machine and place this code into a virtual directory that supports ASP.NET, you will get a screen that looks like Figure 1. It shows the correct current password followed by two passwords in the new password and confirm new password fields that do not agree.

In standard web pages, you might assume that the message about entering and reentering the new password appears only after the Submit button is pressed — but you'd be incorrect. How does this work? Listing One creates this page. While this ASP.NET page might look like ASP, there are significant differences.

Start with the section of the page's source code that creates the second password field to reenter the new password in Example 1. Most of the HTML tags are from the ASP namespace <asp:TextBox>. Some of these controls are similar to their HTML counterparts. For instance, the asp:TextBox control is similar to the HTML Input control where type="text". However, in the second of the two table cells, I use controls that have no equivalence in pure HTML. These controls are validator controls that are set up to display a message when specified conditions are met. In the case of the RequiredFieldValidator, the control referred to by ControlToValidate must have a value entered. In the case of the CompareValidator (which I am using to compare two controls on the form), by default the text in the control specified by the ControlToValidate must be the same as the text in the control specified by the ControlToCompare.

Validator controls are a nice feature of ASP.NET, but they are even better than you might think. For instance, the validator in Figure 1 is displayed when you tab out of the control. How does it do that? The answer to this question can be found in the emitted source code.

In Example 1, you find several <script> blocks. Of course, Listing One has no script blocks. The code in the scripts will be JavaScript, and one of the script blocks contains an include file of some other JavaScript code in a file on the server that contains the web page.

One of the validator types in Table 1 is a CustomValidator, which lets you do validation on the client and server, using custom code on each side. This is often enough, but sometimes you may need something a little more powerful. For those situations, you can create a custom server control that uses both client- and server-side scripting to perform the required function.

Custom Server Controls

One thing many web developers miss is the ability to immediately react to an entry by users in a control. Such was the situation recently when I needed to create a web version of an application that a set of users had long used and become accustomed to. Several of the controls behaved in ways that, until now, would have been difficult to duplicate on the Web. For instance, U.S. Social Security numbers were entered without the dashes, then redisplayed upon exiting the field with the appropriate dashes, enabling data entry people to see that the SSN entered was correct if the number was redisplayed with dashes. If the incorrect number of digits were entered, an error message was displayed. I created a FormatSSN class to encapsulate this functionality.

Listing Two, an ASP.NET page that uses FormatSSN, has no code other than HTML markup and the declaration of the single FormatSSN control. When the web page is loaded and you enter a nine-digit number and tab out of the field, the number is reformatted.

Listing Three (available electronically; see "Resource Center," page 5) contains a C# namespace called DDJ, which contains a class FormatSSN. To create an ASP.NET custom control, you must inherit from a class that descends from the System.Web.UI.Control class. The likely candidate to inherit from this control is the TextBox control. Most of what the control needs to do is what the TextBox control does. However, there is a problem with using the TextBox as a base class: Since I wanted to be able to let the control participate in the normal ASP.NET validation process, I used BaseValidator as the base class. This may not be the best solution, but it does suit the purposes of this example.

Example 2 is the class declaration. In addition to inheriting from the BaseValidator control, the class also implements IPostBackDataHandler and IPostBackEventHandler. C#, like VB.NET, does not allow multiple inheritance. However, both languages do let you inherit from a single class and implement as many interfaces as you like.

Implementing IPostBackDataHandler and IPostBackEventHandler let the control have data posted back to the HTML form whenever the form is posted. Thus, you do not need special logic on your form to repopulate the control after a post. Virtually all ASP.NET server controls are built to properly have the information entered into a control to survive postbacks. While VB or C++ developers may consider this a given, ASP developers spend time making persistent values in controls between calls.

To implement IPostBackDataHandler, I needed to provide an override for the LoadPostData method. This method checks for changes in the data being posted, and returns True if the data has changed or False if it has not. The LoadPostData method accepts two parameters: the postDataKey (a string) and Values (a NameValueCollection). Using the postDataKey as an index into the Values parameter, I can get the posted value for the control.

Any control that inherits from the BaseValidator control must implement a single method, called EvaluateIsValid. This method returns a Boolean value that is used to indicate if the control contains valid information. The framework checks the value of the BaseValidator controls on a page to determine if a page is valid. FormatSSN calls the method of the class called ServerFormatSSN. This method is shown in Listing Two. ServerFormatSSN uses regular expressions to identify either of the properly formatted input strings (nine digits or three digits, dash, two digits, dash, four digits).

One method of any server control that can be used to perform required tasks is the OnLoad method; see Example 3. OnLoad first calls the base OnLoad. This seems to be required to ensure that the base properly performs the required tasks. Next, if this is a postback, I test the format of the entered field, and set the IsValid property to the bIsValid member value, set within ServerFormatSSN. Next, I check to see if the client target of the page is downlevel. If it is not, I emit a JavaScript script block. (I presume that a client target of downlevel will not support JavaScript, so I do not emit it.) I use a special method of the Page class, called RegisterClientScriptBlock, to write the script within the page. While it might seem easier to simply emit the script block, it's essential you use this call to ensure that the script block is written to the page only once, no matter how many FormatSSN controls are on the page. This script simply writes out an include file.

When creating a custom control in ASP.NET, you have two ways to create the control — composition or rendering. With composition, you create child controls that make up the server control. For instance, you could have a text box and validator control created as part of a single control. To use composition, you must override the CreateChildControls method, and use the Add method of the Controls object to add controls to the server control.

FormatSSN uses the second method — rendering. With rendering, you are given total flexibility in what HTML is rendered in the resulting control. Within the overridden Render method, I use the HtmlTextWriter instance (called output in the FormatSSN example) to actually render the HTML. When rendering the control, I do not use a hardcoded value for the Name and ID attributes of the control. Instead, I use this.UniqueID, which is guaranteed to be unique, even if there are multiple instances of the same custom control on the page. It is also critical to render both the ID and Name attributes of the control. If you do not add a Name attribute/value pair in the rendered control, postbacks will not work correctly. In the Render method of FormatSSN, I once again check to see if the browser is downlevel, and if it is not, write out an input box with an OnChange event. This OnChange event will be run on the client, not the server. I set the OnChange event to FormatSSN, a method included in FormatSSN.js (available electronically) inserted as the FormatSSNClientScript script block.

FormatSSN.js tests the string entered into the text box to see if it is in one of the two allowed formats, and if it is, makes sure the string is formatted for display with the dashes. If it is not valid, FormatSSN will display a message box indicating there is a problem. Of course, this behavior can be modified. The client-side code is in an include file. The importance of implementing the control in this way was reinforced when there was a problem with the Microsoft-supplied validation code in the Beta 2 release. Because the code was in an include file and not embedded into the server-side controls, anyone who was bothered by the bug could fix it.

Why Client and Server Validation?

Why provide client and server validation? Doesn't the client validation ensure the value is entered correctly? The answer is "no" — you have no control over the state of the JavaScript engine on the browser. Even for browsers that support JavaScript, it is possible that someone trying to force invalid data into the application can do so by sending back a properly formed URL. Server-side validation ensures that the data is really valid before it is accepted.

Conclusion

One of the things I discovered after creating this control is that there are several possible similar controls. For instance, the system that I needed to emulate formatted dates entered in mmddyy format into m/d/yyyy format. Creating a BaseFormat control that contains support for rendering the control and including a JavaScript script block might be useful. Letting descendants control the JavaScript file included and the properties of the text box would allow descendants to offer almost any type of reformatting. I have not done this yet, but it certainly is something I have considered.

DDJ

Listing One

<%@ Page Language="vb" AutoEventWireup="false" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML>
    <HEAD>
        <title>WebForm1</title>
        <meta content="Microsoft Visual Studio.NET 7.0" name="GENERATOR">
        <meta content="Visual Basic 7.0" name="CODE_LANGUAGE">
        <meta content="JavaScript" name="vs_defaultClientScript">
        <meta 
content=http://schemas.microsoft.com/intellisense/ie5 name="vs_targetSchema">
    </HEAD>
    <body>
    <form id="Form1" method="post" runat="server">
        <table width="500">
            <tr>
                <td align="right" width="33%">Old Password:
                </td>
                <td><asp:textbox id="OldPassword" 
runat="server" 
Width="100" 
TextMode="Password">
</asp:textbox></td>
                <td>
<asp:requiredfieldvalidator 
id="RequiredFieldValidator1" 
runat="server" 
display="Dynamic" ControlToValidate="OldPassword" ErrorMessage="*">
</asp:requiredfieldvalidator></td>
            </tr>
            <tr>
                <td align="right" width="33%">New Password:
                </td>
                <td>
<asp:textbox id="NewPassword" 
runat="server" Width="100" TextMode="Password">
</asp:textbox></td>
                <td>
<asp:requiredfieldvalidator 
id="RequiredFieldValidator2" 
runat="server" display="Dynamic" 
ControlToValidate="NewPassword" 
ErrorMessage="*">
</asp:requiredfieldvalidator>
</td>
            </tr>
            <tr>
                <td align="right" width="33%">Re-Enter:
                </td>
                <td>
<asp:textbox id="NewPassword2" 
    runat="server" Width="100" 
TextMode="Password">
</asp:textbox>
</td>
                <td>
<asp:requiredfieldvalidator 
id="RequiredFieldValidator3" 
runat="server" display="Dynamic" 
ControlToValidate="NewPassword2" 
ErrorMessage="*">
</asp:requiredfieldvalidator>
<asp:comparevalidator
id="CompareValidator1" 
runat="server" 
ControlToValidate="NewPassword2" 
ErrorMessage=
"Please enter and re-enter the same new password" 
ControlToCompare="NewPassword" 
Display="Dynamic">
</asp:comparevalidator>
</td>
            </tr>
            <tr>
                <td align="middle" colSpan="3">
<asp:button id="Button1" Runat="server" 
Text="Submit">
</asp:button>
</td>
            </tr>
        </table>
        </form>
    </body>
</HTML>

Back to Article

Listing Two

<%@ Register TagPrefix="DDJ" Namespace="DDJ" 
Assembly="FormatDateControl" %>
<%@ Page Language="vb" AutoEventWireup="false" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML>
    <HEAD>
        <title>WebForm2</title>
        <meta name="GENERATOR" content="Microsoft Visual Studio.NET 7.0">
        <meta name="CODE_LANGUAGE" content="Visual Basic 7.0">
        <meta name="vs_defaultClientScript" content="JavaScript">
        <meta name="vs_targetSchema" 
                   content="http://schemas.microsoft.com/intellisense/ie5">
    </HEAD>
    <body>
        <form id="Form1" method="post" runat="server">
             
            <table width="500">
                <tr>
                    <td width="50%" align="right">
                        SSN:
                    </td>
                    <td>
                        <DDJ:ReformatSSN 
id="ReformatSSN1" 
runat="server">
</DDJ:ReformatSSN><br>
                    </td>
                </tr>
                <tr>
                    <td colspan="2" align="center">
                        <asp:Button id="Button1" 
runat="server" 
Text="Button">
</asp:Button>
                    </td>
                </tr>
            </table>
        </form>
    </body>
</HTML>

Back to Article


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.