Article Author: JS Greenwood
Introduction
With the introduction of validation controls as part of ASP.NET, a particularly time-consuming, repetitive, and regularly overlooked area of web development was addressed. The validation controls supplied with .NET dealt with the majority of situations out of the box-field validation requirements, comparisons, data-type and range checks, and so on. And for those fields that couldn’t be validated using these simple means, an extension mechanism was provided through the RegularExpressionValidator and CustomValidator controls.
However, whenever a general solution is presented to a problem, it runs the risk of failing to cater for certain less common cases. Functionally, this manifests itself in the difficulty of using the included controls to solve the problems of validating across multiple forms on a page, checking that one of a number of fields is filled in, and so on. Also the validation controls take away some of the ability to control the appearance of error indicators. The ValidationSummary control helps to some extent by providing an alternative to simply displaying text or a symbol (commonly an asterisk) alongside the field, but it still fails to cater for numerous ways of displaying errors.
In this article, I’ll look at this problem-how you can alter the appearance of errors on an ASP.NET web form while still making use of existing validation controls and techniques. Moreover, I’ll show you how to create a solution that requires only the simplest change to existing pages to take advantage of it.
To do this, I’ll first demonstrate how a Page exposes Validators and validation errors. I’ll then show how you can build a framework for handling errors based around this. Next, I’ll make use of the framework to implement two different mechanisms for displaying errors:
- Highlighting the field that contains the error
- Highlighting the label for the field that contains the error
Finally, having produced a working solution that can be used in commercial environments, I’ll take a look at how this can be extended going forward to provide even more options.
System Requirements
To run the included support code, you’ll need a development environment with the following configuration:
- Visual Studio .NET 2003
- IIS 5.0 or above
- .NET Framework 1.1 or above
While these requirements are necessary for the included sample code, the techniques (and even the source code files themselves) should be backward compatible with earlier-and hopefully future-releases of the .NET Framework and Visual Studio .NET.
Installing and Compiling the Sample Code
Within the support code, you’ll find the following files:
- Default.aspx . A web page that demonstrates the first method of altering the appearance of errors.
- Default2.aspx . A web page that demonstrates the second method of altering the appearance of errors.
- Default.aspx.cs . The code-behind page used for both the Default.aspx and Default2.aspx pages.
- TemplatePage.cs . A class used to contain the functionality used for customizing the appearance of errors.
- styles/standard.css . A CSS style sheet to control the appearance of the included web pages.
In order to run the support code, you’ll need to create a virtual directory on your local web server that points to the folder containing the web pages.
Creating a Sample Page
In order to see what you have to work with, you’ll need to create a sample web page. In the support code, this is Default.aspx . The definition of this page (excluding headers, footers, etc.) is as follows:
<fieldset>
<legend>Input form</legend>
<asp:ValidationSummary ID="valSummary" Runat="server"
HeaderText="Errors detected." />
<div class="input">
<label id="lbl_txtUsername">Username:</label>
<asp:TextBox ID="txtUsername" Runat="server" />
<asp:RequiredFieldValidator ID="req_txtUsername"
ControlToValidate="txtUsername"
ErrorMessage="You must enter a username"
Runat="server">*</asp:RequiredFieldValidator>
</div>
<div class="input">
<label id="lbl_txtNumber">Favorite integer:</label>
<asp:TextBox ID="txtNumber" Runat="server" />
<asp:RequiredFieldValidator ID="req_txtNumber"
ControlToValidate="txtNumber" ErrorMessage="You must enter an integer"
Runat="server">*</asp:RequiredFieldValidator>
<asp:CompareValidator ID="cmp_txtNumber" ControlToValidate="txtNumber"
Operator="DataTypeCheck" ErrorMessage="You must enter a valid number"
Type="Integer" Runat="server">*</asp:CompareValidator>
</div>
<div class="input">
<label id="lbl_lstOption">Chosen option:</label>
<asp:DropDownList ID="lstOption" Runat="server">
<asp:ListItem Value="">>Select<</asp:ListItem>
<asp:ListItem Value="Rhubarb">Rhubarb</asp:ListItem>
<asp:ListItem Value="Custard">Custard</asp:ListItem>
</asp:DropDownList>
<asp:RequiredFieldValidator ID="req_lstOption"
ControlToValidate="lstOption" ErrorMessage="You must enter an option"
Runat="server">*</asp:RequiredFieldValidator>
</div>
<div class="options">
<asp:Button ID="btnSubmit" Text="submit" Runat="server" />
</div>
</fieldset>
And in the code-behind, you’ll do nothing more complex than ensure that the page is validated, as follows:
private void btnSubmit_Click(object sender, System.EventArgs e) {
Page.Validate();
}
The Validators Collection
Now, when you run the page and click the submit button without entering any data, you’ll get the validation errors presented back in the standard format, as shown in Figure 1:

Figure 1. Appearance of page with standard validation error rendering
Depending on the browser chosen, these errors will either have been handled on the client, with the screen updated using DHTML (in the case of IE 5.0 and above); or a postback will have occurred, and the new errors will have been detected by the controls in the code-behind (in the case of alternative browsers such as Firefox and Opera). Even if a non-Microsoft browser is used and a postback is generated, you’ll still never get to see any processing other than your call to Page.Validate() . Even that isn’t strictly necessary in this scenario-this method has already been called automatically by the page as part of its life cycle.
So, you’ll usually have little need to or scope for interaction with the validators. However, in order to alter the appearance of errors, you’ll need to know a few things:
- Which validators are on the page
- Which controls the validators relate to
- Whether the validation passes for each validation control
Thankfully, this information is readily available, being exposed via the Page.Validators collection. Each validator on the page (including those on any sub-controls) is in this collection. As all validation controls inherit from BaseValidator , you can update your btnSubmit_Click() event handler to iterate through the controls as follows:
private void btnSubmit_Click(object sender, System.EventArgs e) {
Page.Validate();
const string TEMPLATE = "Validator: {0} -> {1}: {2}";
foreach (BaseValidator val in this.Validators) {
Debug.WriteLine(string.Format(TEMPLATE,
val.ID, val.ControlToValidate, val.IsValid));
}
}
When the code is run while a debugger is attached, the output window that appears after a postback will display the information shown in Figure 2 (assuming no data has been entered).

Figure 2. Debug window output showing success of each validation
One interesting thing to note here is when validator cmp_txtNumber control. With no data entered, it has a value of True . A null/empty value for a field passing validation occurs for all ComparisonValidator s, RangeValidator s, etc. It’s assumed that if entering a value in the field is necessary, it will be used in conjunction with a RequiredFieldValidator .
Creating a Framework
So, knowing that I have the Page.Validators collection to work with, I need to find a way of enabling errors to render differently. Hard-coding an implementation for this on each page, or even updating pages to make calls to external functions, isn’t a particularly transparent approach, however. To really ensure a standardized appearance for errors across a site, I need something a little more structured and implicit.
To achieve this, it’s possible to create a base class that all pages inherit from. By default, every page defined inherits from System.Web.UI.Page :
public class _Default : System.Web.UI.Page {
…
What I can do is define a new clas, TemplatePage , that inherits from this class, and then update the web page’s code-behind to inherit from this TemplatePage . As well as creating the class, I also need a location to "hook into" the rendering pipeline where I can update the appearance of controls on the page. The place in the pipeline chosen is the OnPreRender() method-as it’s called after all page-level (and control-level) events have been processed. Also at this point, all normal user-defined routines that may have interacted with the validation controls have executed. A basic definition of this class can be seen in the following code block.
namespace ValidationControlAppearance {
/// <summary>
/// A base class to allow global styling of fields with errors
/// </summary>
public class TemplatePage : Page {
protected override void OnPreRender(EventArgs e) {
//TODO: Clear previous error appearance
//TODO: Set new error appearance
base.OnPreRender(e);
}
}
}
With this implemented, all that needs to be done to any web page is to apply the custom error styling by changing the class it’s inherited from, as in the modification to the _Default class, as follows:
public class _Default : TemplatePage {
…
With this done, the site will continue to perform exactly as before, only now there will be a common place to drop code into for dealing with errors in whatever way you choose.
Rendering Errors Differently
With the framework created, I can turn my attention to the required contents of the OnPreRender() method. To understand the need for the two TODO comments-one to remove any previous appearance settings applied to the error controls, and one to apply new ones-let’s look at a simple example. Take two validators, Val1 and Val2 , both of which are related to a text box, txt . If I loop around these validators, it first comes to Val1 . Assuming this validator fails, I’ll do whatever is necessary to flag the field as invalid. Next, I’ll come to Val2 , which, for argument’s sake, I’ll say passes. Now, the natural thing to do is mark the field as valid, but I can’t, since the previous validator failed. So, I remove the modifications to the appearance of the control caused by any errors (which I do in case ViewState is enabled, persisting errors from the previous generation of the page). If any of the validators fail after resetting the control appearance, I then amend the appearance of it to show it’s invalid.
Highlighting Fields that Contain Errors
So, to put this theory into practice, I need an alternative way of displaying errors. To try something different from standard websites, I take a cue from Macromedia’s Flex tool set and UI theme, and highlight the fields themselves with a red border. (I also change the background color, since not all browsers support border settings). From a UI point of view, I’ll define this within a CSS style sheet as a new class, error , that will get applied to <input /> and <select /> elements:
div.input input.error,
div.input select.error { border: 2px solid red; background-color: #ffcccc;
}
This can now be applied to the CssClass property of each WebControl that is referenced by a validator that fails validation.
protected override void OnPreRender(EventArgs e) {
// Clear previous error appearance
foreach (BaseValidator val in this.Validators) {
WebControl control = (WebControl) FindControl(this,
val.ControlToValidate);
if (control==null) { continue; }
control.CssClass = control.CssClass.Replace("error", "").Trim();
val.EnableClientScript = false;
val.Text = " ";
}
// Set new error appearance
foreach (BaseValidator val in this.Validators) {
WebControl control = (WebControl)
FindControl(this, val.ControlToValidate);
if (control==null) { continue; }
if (!val.IsValid) {
string spacer = (control.CssClass+"" "") ? "" : " ";
control.CssClass += spacer + "error";
}
}
base.OnPreRender(e);
}
Looking at this code, you can see it's fairly simple. The following steps are performed:
- I loop around all the validators and remove any mention of error in the CssClass property of each referenced control. I also disable the client script for each validator to ensure that this code is processed every time the submit button is clicked. This will ensure that all validations occur on the server so that the field-highlighting code is executed. One final task is to set each validator's Text to . This removes the asterisk that would otherwise show when an error is detected. In a production system, you may want to ensure that you only remove the text error when it occurs as a full word, not just as any reference. Similarly, you may wish to implement a client-side model of the approach taken here using a document.getElementById() JavaScript call to return the control you wish to style.
- Next, I loop around all of the validators again, and for each control I determine if the validator passes validation, adding the link to the error CSS class if the validation fails.
The code above makes use of one extra method: FindControl() . This simply recurses through the controls on the page and finds the control with the specified ID (as opposed to the built in FindControl() method, which doesn't recurse through child controls). The definition for this method is as follows:
private Control FindControl(Control parent, string id) {
if (parent.IDid) { return parent; }
foreach (Control child in parent.Controls) {
Control recurse = FindControl(child, id);
if (recurse!=null) { return recurse; }
}
return null;
}
With all of this in place, I can recompile the program, refresh the web page, and click submit again. The results are shown in Figure 3.

Figure 3. Appearance of page with invalid fields directly highlighted
Amending Field Labels
As a second example of this, I’ll do something slightly different-rather than altering the fields containing the errors, I’ll highlight the labels for the field. But in the process, I’ll show how something like this can be achieved using exactly the same pattern. The support code contains the page Default2.aspx , which demonstrates this technique.
To make this possible, I first need access to the <label /> elements on the Default.aspx page. To achieve this, each control needs to be given a runat="server" attribute-here, I’ll make use of the fact that the ID value I’ve given to each label follows the pattern lbl_<fieldName /> . Here’s an example of an updated field definition:
<div class="input">
<label id="lbl_txtUsername" runat="server">Username:</label>
<asp:TextBox ID="txtUsername" Runat="server" />
<asp:RequiredFieldValidator ID="req_txtUsername"
ControlToValidate="txtUsername" Runat="server"
ErrorMessage="You must enter a username">*</asp:RequiredFieldValidator>
</div>
In the OnPreRender() method, I have to make a few amendments due to the control I’m now dealing with. First, I need to alter the name of the control I’m looking for by prefixing it with the text lbl . Then, rather than inheriting from WebControl , a <label /> element inherits from the HtmlControl class-so I need to make use of this. The HtmlElement class doesn’t expose a CssClass method either, so I’ll need to make use of the Attributes property to assign it manually.
While I’m at it, I can get rid of the ValidationSummary from the top of the page, which will save onscreen real estate. While this might not be important on a small form such as this, it can be a useful technique on a longer form that might otherwise require scrolling to check over the fields and errors. To display the error messages when you hover over an error label (i.e., a tooltip) instead, I give each <label /> element a title attribute, with the ErrorMessage property of the validator being used to populate it.
This is all shown in the code that follows.
protected override void OnPreRender(EventArgs e) {
// Clear previous errors
foreach (BaseValidator val in this.Validators) {
HtmlControl control = (HtmlControl) FindControl(this,
"lbl_" + val.ControlToValidate);
if (control==null) { continue; }
control.Attributes["class"] = (control.Attributes["class"] +
"").Replace("error", "").Trim();
control.Attributes["title"] = "";
val.EnableClientScript = false;
val.Text = " ";
}
// Set new errors
foreach (BaseValidator val in this.Validators) {
HtmlControl control = (HtmlControl) FindControl(this, "lbl_" +
val.ControlToValidate);
if (control==null) { continue; }
if (!val.IsValid) {
string spacer = (control.Attributes["class"]+"" == "") ? "" : " ";
control.Attributes["class"] += spacer + "error";
control.Attributes["title"] += val.ErrorMessage + " ";
}
}
base.OnPreRender(e);
}
With this done, one final amendment to the CSS file is then needed to ensure the labels are updated appropriately-setting the text to be highlighted in red and bold:
div.input label.error {
color: #ff0000;
font-weight: bold;
}
That’s it. Figure 4 shows the effect of clicking the submit button on the half-completed form after I recompile and switch back to the browser again.

Figure 4. Appearance of page with labels showing invalid fields
Further Work
There are many possibilities for how to take the customization of displaying errors forward. The exact methods and features required will depend on the individual site-the browsers targeted, the consistency required with existing styling, and so on. As a couple of examples, the following options are potential avenues of exploration:
- As well as altering how each individual error is rendered, you could develop your own ValidationSummary control that groups items together. This could allow you to display further information-possibly in an expandable area for each item. You could give examples of valid data, and so on.
- As it stands, the current implementation requires a postback in order to facilitate its amendments to the pages. This is achieved by setting the EnableClientScript property of each validator to False . Client-side support could be developed to provide the same functionality in the browser itself to improve the user experience and reduce the server load.
Conclusion
In this article, I first demonstrated how validators work-how some of them only fire when the control they’re related to has data in it. I showed the Validators collection exposed by the page, and how it contains all the validation controls that are present. Next, I showed how to create a base class for pages to abstract the logic for handling the display of errors away from the individual pages. This class then had the OnPreRender() method overridden to allow for the updating of the page’s appearance following page processing. I achieved this first by looping around the validators, finding the controls associated with them, and applying the necessary styling. Later, I showed how to relate other controls to validators to enable styling of the labels for fields.
The techniques demonstrated here should provide an introduction to the possibilities that exist for extending how validation errors are shown to the user, allowing for more control over the visual appearance than is provided with the built-in controls.

