View demo | Download source

The default validators inside ASP.NET Web Forms are quite uninteresting and require some styling work to look adequate.  Recently, we’ve been using the qTip2 jQuery library and we love it.  qTip enables you to add visually pleasant tooltips to any element.  For example, you simply add a “title” attribute to any element and then apply qTip to this element and the “title” attribute will be used as the tooltip’s text.  This is the simplest use case.  Here’s an example; with our FranchiseBlast registration form.

image

When you try to submit this form and the validation doesn’t pass, we replaced the default ASP.NET validators with styled qTip tooltips beside each validated element.

SNAGHTML6770ee44

Like you can see, the validators have absolute positioning, which enables them to flow outside of the bounds of the registration panel.  We could also easily change the position of the bubble in relation to the validated element and also change the bubble tip position.

Let’s take a look at what was needed to accomplish this, using a simple ASP.NET project. Here is the main ASP.NET code for the ASPX page.  Nothing fancy: a simple form with some validators:

Default.aspx

<asp:ScriptManager ID="p" runat="server">
    <Scripts>
        <asp:ScriptReference Path="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js" />
        <asp:ScriptReference Path="~/Scripts/qtip/jquery.qtip.min.js" />
        <asp:ScriptReference Path="~/Scripts/validators.js" />
    </Scripts>
</asp:ScriptManager>
<fieldset class="Validate" style="width: 300px">
    <legend>Tell us about yourself</legend>
    <div>
        <span class="label">Business Name:</span>
        <asp:TextBox ID="txtBusinessName" runat="server" />
        <asp:RequiredFieldValidator ID="rfvBusinessName" runat="server" ControlToValidate="txtBusinessName" Text="Your business name is required" SetFocusOnError="true" EnableClientScript="true" />
    </div>
    <div class="alternate">
        <span class="label">Your Name:</span>
        <asp:TextBox ID="txtYourName" runat="server" />
        <asp:RequiredFieldValidator ID="rfvName" runat="server" ControlToValidate="txtYourName" Text="Your name is required" SetFocusOnError="true" EnableClientScript="true" />
    </div>
    <div>
        <span class="label">Your Email:</span>
        <asp:TextBox runat="server" ID="txtEmail" />
        <asp:RequiredFieldValidator ID="rfvEmail" runat="server" ControlToValidate="txtEmail" Text="Email is required" SetFocusOnError="true" EnableClientScript="true" />
        <asp:RegularExpressionValidator runat="server" ID="revEmail" Text="Invalid Email" ControlToValidate="txtEmail" SetFocusOnError="true" ValidationExpression="^([0-9a-zA-Z]([-.\w]*[0-9a-zA-Z])*@(([0-9a-zA-Z])+([-\w]*[0-9a-zA-Z])*\.)+[a-zA-Z]{2,9})$" EnableClientScript="true" />
    </div>
</fieldset>
<asp:Button runat="server" ID="btnCreateAccount" CssClass="Next" Text="Create Account" />

Starting from the top, we need jQuery and also qTip to be added to our page.  The interesting JavaScript code in located in ~/Scripts/validators.js.  The rest of the code here is a simple ASP.NET form.  One important thing is that each element to be validated is enclosed in a <div> with his corresponding validators.  This is important because we will use this convention later in our script to find the associated validators for an input control.

I also have to mention that I added some lines in the .skin file of the App_Theme:

Default.skin

<asp:RequiredFieldValidator runat="server" CssClass="ErrorMsg" Display="Dynamic" />
<asp:CustomValidator runat="server" CssClass="ErrorMsg" Display="Dynamic" />
<asp:RangeValidator runat="server" CssClass="ErrorMsg" Display="Dynamic" />
<asp:CompareValidator runat="server" CssClass="ErrorMsg" Display="Dynamic" />
<asp:RegularExpressionValidator runat="server" CssClass="ErrorMsg" Display="Dynamic" />

This will force CssClass=”ErrorMsg” on validators.  This will be used next in our JavaScript code to find the validators:

validator.js

Sys.WebForms.PageRequestManager.getInstance().add_pageLoaded(function () {
    function getValidator() {
        return $(this).parent().find('.ErrorMsg').filter(function () { return $(this).css('display') != 'none'; });
    }
 
    var inputs = '.Validate input:text, .Validate select, .Validate input:password';
 
    var submit = $('input:submit');
 
    var q = $(inputs).qtip({
        position: {
            my: 'center left',
            at: 'center right'
        },
        content: {
            text: function (api) {
                return getValidator.call(this).html();
            }
        },
        show: {
            ready: true,
            event: 'none'
        },
        hide: {
            event: 'none'
        },
        style: {
            classes: 'ui-tooltip-red ui-tooltip-shadow ui-tooltip-rounded'
        },
        events: {
            show: function (event, api) {
                var $this = api.elements.target;
                var validator = getValidator.call($this);
                if (validator.length == 0)
                    event.preventDefault();
            }
        }
    });
 
    if (window.Page_ClientValidate != undefined) {
        function afterValidate() {
            $(inputs).each(function () {
                var validator = getValidator.call(this);
 
                if (validator.length > 0) {
                    var text = validator.html();
 
                    $(this).addClass('Error').qtip('show').qtip('option', 'content.text', text);
//                    validator.hide();
 
                }
                else
                    $(this).removeClass('Error').qtip('hide');
            });
        }
 
        $(inputs).blur(afterValidate);
 
        var oldValidate = Page_ClientValidate;
 
        Page_ClientValidate = function (group) {
            oldValidate(group);
 
            afterValidate.call(this);
 
            submit.removeAttr('disabled');
        }
    }
});

There is much to explain in this code.  First we register a new function to be executed each time there’s an ASP.NET PostBack on the page here: Sys.WebForms.PageRequestManager.getInstance().add_pageLoaded(function () { … });

The function getValidator finds the visible ASP.NET validators associated to a control to be validated.  We use the fact that the control to validate and the validators are contained inside a <div>.

We apply qTip to the inputs to validate and we get the text of the message by finding the visible validators.  Also we have some logic to prevent showing the qTip element if there aren’t any visible validators.

We also do some monkey patching at the end where we inject our own code inside the Page_ClientValidate ASP.NET JavaScript method.  To do that, we simply get a reference to the Page_ClientValidate function, create a new function with our additional code (calling the old Page_ClientValidate) plus we override window.Page_ClientValidate with our new function.  This new function have both the new and old functionality.

You would probably have to modify this code a little bit to fit your needs, but this shows how you could integrate qTip2 for nicer validators in ASP.NET Web Forms.

View demo | Download source