Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / HTML

Enhancing the presentation of standard validator outputs

4.68/5 (26 votes)
13 Jul 20068 min read 4   759  
How flexible is the normal functionality of ASP.NET validators? In this article, I am going to show how to customize the appearance of the attached control of a validator during an error situation on the server or the client side, or even call a custom client function without postback.

Describing the problem

In a project I am working in, I was faced with a problem. The client asked to change the default presentation of the errors that appeared. He wanted to highlight a background of the field with a custom color when a user entered wrong data on a form. The ordinary technique to show that the validation process failed is to show some red text in a certain position, or just an asterisk. In addition to this, we can also place a ValidationSummary control to show the summarized output. I was searching for some built-in features in .NET to run a custom client function before the postback, but without success.

Sample Image - highlight_validators.jpg

In this article, I am going to show how to add a special Cascading Style Sheet class name to an attached control in an error situation, on the server and the client sides, or even call a custom client function. In addition, I am going to show how to customize validation summary results.

You can download the sources (10.9 Kb), and easily add the functionalities to your form. There is a class library called HighlightValidators. It contains all types of enhanced validators like RequiredValidator, CompareValidator, RangeValidator, RegularExpressionValidator, and CustomValidator. In the demo app (6.52 Kb), you can also find an example form.

How to use the solution

To use it, you need to go through these steps:

  1. Copy UserControls.HighlightValidators.dll from the HighlightValidators/bin/Release folder to the bin folder of your web-application.
  2. Add the following block into the web.config file:
    XML
    ...
    <pages>
     <controls>
      <add tagPrefix="uc" assembly="UserControls.HighlightValidators" 
           namespace="UserControls.HighlightValidators" />
     </controls>
    </pages>
    ...

    It will register the Highlight controls from the UserControls.HighlightValidators assembly for the whole web-application.

  3. After that, you can declare the HighlightRequiredValidator, for instance, as follows:
    HTML
    <uc:HighlightRequiredValidator id=HighlightRequiredValidator1 
     ErrorCssClass="on_error" ErrorMessage="First Name is required" 
     ControlToValidate="TextBox1" runat="Server" />

    I set ErrorCssClass to "on_error". Now, when validation fails, the control TextBox1 will be customized according to the on_error class.

  4. All Highlight validators also have the ErrorClientFunction property. You can use it to call custom client functions in an error situation:
    HTML
    <uc:HighlightCompareValidator ID="HighlightCompareValidator1" runat="server" 
      ControlToValidate="TextBox1" ErrorMessage="Number of books must be numeric" 
      ErrorCssClass="on_error" ErrorClientFunction="onError" 
      Operator="DataTypeCheck" Type="Integer" />

    Now, you need to declare the function onError() on the page:

    JavaScript
    function onError(val)
    {
        alert("Number of books must be numeric");
    }

    Please note that this function takes a parameter, val. This object describes the validator in which the validation has failed. For more information, read the section below.

Diving in to the code

To enhance the standard validators, I used them as inherited classes, and created a new branch of validators with the “Highlight” prefix, because I just wanted to add some desired functionality and maintain compatibility so developers can easily switch between the standard ASP.NET validators and the enhanced Highlight validators.

At first, let’s take a look at how changing the class name works. There are two parts of the code – server and client sides. With the server side, everything is easy: I just added a check whether the validator is valid or if the validation process failed. If it isn’t valid, I add an error class name to the attached control:

C#
protected override void OnPreRender(EventArgs e)
{
    WebControl control = (WebControl)Parent.FindControl(ControlToValidate);
    ClassNameHelper.Remove(control, this.errorCssClass);
    if (!IsValid)
    {
        ClassNameHelper.Add(control, this.errorCssClass);
    }

    base.OnPreRender(e);
}

The ClassNameHelper.Remove() method deletes the error class name in any case, because the previous postback could have failed validation and the class name was added. Now, we need to remove it first, and then check again. If IsValid equals false again, then we need to add the error class name again using the ClassNameHelper.Add() method.

Nothing interesting so far. But let’s figure out how to change the class name of the attached control on the client side before the user postbacks the form. As I said in the introduction section, if we set EnableClientScript to true (its default value), then in case the user enters wrong data, the validation client scripts do not postback the form to the server and add some red text error description or show a validation summary. To investigate this process, I went to the aspnet_client folder and looked into the WebUIValidation.js script file.

I found there, the ValidatorUpdateDisplay function:

JavaScript
function ValidatorUpdateDisplay(val) {
    if (typeof(val.display) == "string") {    
        if (val.display == "None") {
            return;
        }
        if (val.display == "Dynamic") {
            val.style.display = val.isvalid ? "none" : "inline";
            return;
        }
    }
    val.style.visibility = val.isvalid ? "hidden" : "visible";
}

That’s it! Client scripts use it to show/hide the red error message of the validator. So if we can override it, we can add some additional code, and that will help to implement the enhanced functionalities. To achieve this, I used a little trick – a hijacking. For example, if we have a function test in some included JavaScript file, and we need to override it in the some HTML file, we can do this:

JavaScript
var oldTest;
function newTest()
{
    // call oldTest function
    oldTest();
    
    // execute some additional code
    alert("we've overrided it");
}
window.onload = function()
{
    // hijack test() method
    oldTest = test;
    test = newTest;
}

When the page loads (window.onload event), we save the test() function pointer in the oldTest variable, and replace the current pointer of test() with the newTest() pointer. So now, if we call the test() function, actually we are calling the oldTest() function. Please note that if you use the name of a JavaScript function without parentheses, it’s only a pointer, but if you need to call the function, you need to use “()” at the end.

Let’s go back to the ValidatorUpdateDisplay() function. Now we know how to override it. We also have the val object as a parameter. This object describes the validator which is in the validation process and which changes state after some user action. Some of the important properties of the validator object are described in the following table:

IDEquals the ClientID property of the validator
controltovalidateContains the ClientID of the control which is attached to the validator
isvalidIndicates whether the validation process failed or not
errormessageContains the ErrorMessage property

Now, I am going to override the ValidatorUpdateDisplay() function. I need to go through the list of all the Highlight validators placed on the current form and add custom error class names to the controls, in case the validation fails (isvalid == false). I use the Page_HighlightValidators array to save all necessary information about a Highlight validator (validator ClientID, ErrorCssName, and others) to be accessible for client scripts. This happens during the OnLoad event of the Highlight validator:

C#
private string errorCssClass;
public string ErrorCssClass
{
    get { return this.errorCssClass; }
    set { this.errorCssClass = value; }
}

private string errorClientFunction;
public string ErrorClientFunction
{
    get { return this.errorClientFunction; }
    set { this.errorClientFunction = value; }
}

protected override void OnLoad(EventArgs e)
{
    ClientScriptManager cs = Page.ClientScript;
    ...
    Control control = Parent.FindControl(this.ControlToValidate);
    if (control != null)
    {
        if (!cs.IsClientScriptBlockRegistered(this.GetType(), 
             String.Format("HighlightValidation_{0}", this.ClientID)))
        {
            cs.RegisterClientScriptBlock(this.GetType(), 
                String.Format("HighlightValidation_{0}", this.ClientID), 
                String.Format("Page_HighlightValidators.push(
                              new Array('{0}', '{1}', '{2}', '{3}'));", 
                this.ClientID, control.ClientID, this.errorCssClass, 
                this.errorClientFunction), true);
        }
    }
    ...
}

I use the push() method of the Array object to add a new item to the Page_HighlightValidators array because I actually do not know the order of the placed Highlight validators on the form. ErrorCssClass and ErrorClientFunction are new properties of the Highlight validators. We can use them to set custom error class names and client function names that we need to run in case the validation fails.

Eventually, I can use a loop in an already overridden ValidatorUpdateDisplay() function, and add custom error class names if the validation fails:

JavaScript
function newValidatorUpdateDisplay(val)
{
    // call hijacked ValidatorUpdateDisplay() method
    oldValidatorUpdateDisplay(val);
    
    for (i = 0; i < Page_HighlightValidators.length; i++)
    {
        if (val.id == Page_HighlightValidators[i][0] && 
            val.controltovalidate == Page_HighlightValidators[i][1])
        {
            var control = document.getElementById(Page_HighlightValidators[i][1]);
            var errorClassName = Page_HighlightValidators[i][2];
            var errorCallFunction = Page_HighlightValidators[i][3];
            
            // add class and change tooltip
            
            if (errorClassName != "")
            {
                RemoveClassName(control, errorClassName);
                if (!val.isvalid)
                {
                    AddClassName(control, errorClassName);
                    control.title = val.attributes.title.value;
                }
            }
            
            // call custom function            
            if (errorCallFunction != "" && 
                eval("typeof("+errorCallFunction+")") == "function")
            {
                if (!val.isvalid)
                {
                    eval(errorCallFunction+"(val)");
                }
            }
        }
    }
}

Instead of just replacing the class name with the error class name, I use the RemoveClassName and AddClassName functions which have the ability to add and remove class names. They have the ability to attach a few class names, separating them by spaces, like this:

HTML
<div class="class_one class_two">...</div>

Please note also that the properties from the class_two class override the properties from class_one. So, we just change the properties which indicate the error, and do not destroy others.

In the code above, I also added calling the custom error client function, and pass the val object to it as the parameter.

Custom validation summary on the client side

Now, we've enhanced the validators, and can indicate to the customer who uses our form the kind of errors that happen, in any way we like, using custom error class names and custom client functions. But let's take a look at the ValidationSummary control. How can its default functionalities be made flexible for us?

For example, I want to place a ValidationSummary control as a div in the center of the browser's window and customize its appearance. For now, let's forget about the asp:ValidationSummary control. And let's go back to the aspnet_client folder again, and open the WebUIValidation.js file. There is a ValidationSummaryOnSubmit() function which the ValidationSummary control calls when it's needed to display the errors list. It's easy to find out that this function uses a global array, Page_Validators, which contains the collective information about all the validators on the current form. OK, that’s what we need. We can use this array to go through all the validators and check their state.

Next, I am going to create a div for my custom Validation Summary control:

HTML
<div id="SummaryValidation">
    <span>
        <p style="font-weight:bold">The following errors were occured:</p>
        <ul id="ValidationProblems"></ul>
    </span>
</div>

I already added a header and a list tag, where I am planning to display all the errors. To center the div in the browser window, I am using a little trick. For more details about this, read this perfect article: Horizontally Centered Absolute Positioning.

In the next step, we need to attach the JavaScript function to the Submit event of the form, where we will check all the validators and create a list of errors if they are present; in the other case, we just let the form be posted to the server:

C#
protected void Page_Load(object sender, EventArgs e)
{
    ClientScriptManager cs = Page.ClientScript;
    if (!cs.IsOnSubmitStatementRegistered(this.GetType(), 
         "PrepareValidationSummary"))
    {
        cs.RegisterOnSubmitStatement(this.GetType(), 
           "PrepareValidationSummary", "CheckForm()");
    }
}

Now, I declare the CheckForm() function as follows:

JavaScript
function CheckForm()
{
    if (!Page_IsValid)
    {
        var s = "";
        for (i=0; i<Page_Validators.length; i++) {
            if (!Page_Validators[i].isvalid && 
                 typeof(Page_Validators[i].errormessage) == "string") {
                s += "<li>" + Page_Validators[i].errormessage + "</li>";
            }
        }
        if (s != "")
        {
            document.getElementById('ValidationProblems').innerHTML = s;
            document.getElementById('SummaryValidation').style.display = 'inline';
        }
    }
}

In CheckForm(), I check all the validators for validity and collect error information. Then, if at least one error is present, I display the validation summary.

To hide the validation summary, create the HideValidationSummary() function, and attach it to the document.onlick event so it will disappear after the customer clicks somewhere on the browser’s window.

I hope it is helpful to you, and will appreciate if you leave your opinions, corrections, or any other thoughts. Thanks for your time.

Change history

  • 7/7/2006 - Refactoring: added the EventHelper class to share some recurring blocks of code which are present in each highlight validator declaration.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here