Using the ValidatorCallout control in Combination with ASP.NET 2 Templated Controls

Now that the release date for the final version of ASP.NET AJAX 1.0 is getting closer (RC1 was released on December 14 2006), I decided to dig a little deeper in its feature set and see what it has to offer. I also looked at the ASP.NET AJAX Control Toolkit and the new controls that are part of this toolkit.

One of those new controls is the ValidatorCallout extender control that displays a big yellow call out with the error message.

Introduction

If you haven't seen the control at work yet, take a look at this live demo. Leave the User Name and Password fields blank and click the Log In button. You'll see the following screen appear:

The Login Control with a ValidatorCalloutExtender
Figure 1 - The Login Control with a ValidatorCalloutExtender

Instead of the standard red error message, you now get a good looking yellow call out with an attention icon and the original error message.

Using the ValidatorCallout is pretty easy. Just add it to an ASP.NET AJAX Control Toolkit enabled site, set its TargetControlID property to the ID of your validator control and you're done. The markup could be as simple as this:

<ajaxToolkit:ValidatorCalloutExtender 
  runat="Server" 
  ID="vceUserName" 
  TargetControlID="reqUserName"
/>    

In addition to the three attributes above, you have a few other attributes available, like Width (to determine the width of the call out), HighlightCssClass (contains the CSS class that is applied to the control whose value is invalid (like the TextBox for the user name in Figure 1), WarningIconImageUrl (the icon that is shown in the call out in case you don't like the default yellow icon) and CloseImageUrl (that can contain a small Close image to replace the existing one at the top right-hand corner of the call out.

So, using the control as per the instructions on its demo page is dead easy. However, getting the ValidatorCallout to work with standard, templated ASP.NET controls like the <asp:Login> control is a bit trickier.

If you have an <asp:Login> control on a page, all you see is the following markup:

<asp:Login ID="Login1" runat="server"></asp:Login>    

(Your code may be a bit more verbose as it's easy to set additional attributes on the control, like event handlers). If you view a page with this control in a browser, you do get automatic validation. You can't log in to the site without entering a user name and a password. So, somehow this control seems to contain a number of RequiredFieldValidator controls. But where?

The answer is in the template code for the control. By default, the Login control (and other template based controls) doesn't show the markup that it will render at run-time. Instead, it shields you from all the complex HTML that is necessary to draw the control in a browser. This is a good thing, as you often don't need to see all this HTML, and instead want to use the control's designer capabilities to format it. But, looking at the HTML can help you in understanding what the control does and how it works. Fortunately, getting that HTML is very easy: switch the page that contains the control into Design View, open its Smart Task pane by clicking the little black arrow and choose Convert to Template. The control will expand its code so you can see how it's build up. In Code View you now see a <LayoutTemplate> element that holds an HTML <table> that in turn contains a number of controls, like TextBox controls for the user name and password, a Login button to trigger the login action and two <asp:RequiredFieldValidator /> controls that are used to make sure you fill in a user name and a password.

If you don't mind the additional markup for the Login or other template based controls in your page, you're pretty much done now. All that's left to do is add two <ajaxToolkit:ValidatorCalloutExtender /> controls to the <LayoutTemplate> template of the Login control and set their TargetControlID properties to UserNameRequired and PasswordRequired respectively. You should end up with code similar to this:

<asp:Login ID="Login1" runat="server">
  <LayoutTemplate>
    <ajaxToolkit:ValidatorCalloutExtender 
        runat="Server" ID="reqUserName" TargetControlID="UserNameRequired" />
    <ajaxToolkit:ValidatorCalloutExtender 
        runat="Server" ID="reqPassword" TargetControlID="PasswordRequired" />
    <table border="0" cellpadding="1" cellspacing="0" 
                 style="border-collapse: collapse">
    <tr>

      ... Snip ...

      <asp:TextBox ID="UserName" runat="server"></asp:TextBox>
      <asp:RequiredFieldValidator ID="UserNameRequired"
           runat="server" ControlToValidate="UserName" 
           ErrorMessage="User Name is required." 
           ToolTip="User Name is required." ValidationGroup="Login1">*
      </asp:RequiredFieldValidator>

      ... Snip ...

      <asp:TextBox ID="Password" runat="server" TextMode="Password"></asp:TextBox>
      <asp:RequiredFieldValidator ID="PasswordRequired" 
           runat="server" ControlToValidate="Password" 
           ErrorMessage="Password is required." 
           ToolTip="Password is required." ValidationGroup="Login1">*
      </asp:RequiredFieldValidator>


      ... Snip ...

    </tr>
    </table>
  </LayoutTemplate>
</asp:Login>

Notice I only posted the relevant parts of the markup for the Login control. The download you find at the end of this article contains a fully working example with all the code.

To make the Login and ValidatorCallout control look a little nicer, you need to hide the original red error message. You can do this by setting the Display property of the RequiredFiledValidator to None:

<asp:RequiredFieldValidator ID="UserNameRequired" 
    Display="None" 
    runat="server" ControlToValidate="UserName" 
    ErrorMessage="User Name is required." 
    ToolTip="User Name is required." ValidationGroup="Login1">*
</asp:RequiredFieldValidator>

Now when the call out is shown, you no longer see the red error message behind it.

Changing the ValidatorCallout Extenders at Run-Time

Whether the example presented above works for you is mostly a matter of preference. Personally, I don't like the large amount of markup that has been inserted in my page by converting the <asp:Login> control to a template. Instead, I prefer to have the much cleaner alternative. However, with the short version of the control, you can't set the ValidatorCallout TargetControlID property in the markup anymore. Instead, you need to write some code in the Code Behind for the page to set those properties dynamically at run-time. The main benefit of doing this through code is that you avoid page clutter. With the code based solution your page can look as clean as this:

<form id="form1" runat="server">
<div>
  <asp:ScriptManager ID="ScriptManager1" runat="server" />
  <ajaxToolkit:ValidatorCalloutExtender runat="Server" ID="reqUserName" />
  <ajaxToolkit:ValidatorCalloutExtender runat="Server" ID="reqPassword" />
  <asp:Login ID="Login1" runat="server" />
</div>
</form>

Notice how these ValidatorCalloutExtender controls don't have their TargetControlID set. This is now done in the code behind with this code in the Page_Load event handler:

C#
protected void Page_Load(object sender, System.EventArgs e)
{
  RequiredFieldValidator valUserName = 
        (RequiredFieldValidator)Login1.FindControl("UserNameRequired");
  RequiredFieldValidator valPassword = 
        (RequiredFieldValidator)Login1.FindControl("PasswordRequired");

  reqUserName.TargetControlID = valUserName.UniqueID;
  reqPassword.TargetControlID = valPassword.UniqueID;

  valUserName.Display = ValidatorDisplay.None;
  valPassword.Display = ValidatorDisplay.None;
}


VB.NET
Protected Sub Page_Load(ByVal sender As Object, _
	  ByVal e As System.EventArgs) Handles Me.Load  
  Dim userNameRequired As RequiredFieldValidator = _
	  CType(Login1.FindControl("UserNameRequired"), RequiredFieldValidator)
  Dim passwordRequired As RequiredFieldValidator = _
	  CType(Login1.FindControl("PasswordRequired"), RequiredFieldValidator)

  reqUserName.TargetControlID = userNameRequired.UniqueID
  reqPassword.TargetControlID = passwordRequired.UniqueID

  userNameRequired.Display = ValidatorDisplay.None
  passwordRequired.Display = ValidatorDisplay.None
  
End Sub  

The first two lines of code (split over four lines in this article) use the FindControl method of Login1 to get a reference to the RequiredFieldValidators for the user name and password respectively.

The RequiredFieldValidators controls are then used to set the TargetControlID properties of the ValidatorCalloutExtender controls. Notice how I am using UniqueID and not the normal ID property. Because the validators live in the naming container of the Login control, you need to get a name that takes this naming container into account. UniqueID returns something like Login1$UserNameRequired which is the full and unique name of the control.

The final two lines of code set the Display property of the validators to None to avoid the standard error message from appearing. This is similar to setting the Display attribute in the markup as you saw earlier.

Summary

ASP.NET AJAX 1.0 is a very promising and fun technology, especially in combination with the ASP.NET AJAX Control Toolkit. The two combined offer great possibilities for creating cool looking and functional web UIs. Because they're based on standard .NET and ASP.NET practices, these technologies are pretty easy to learn. As you saw in this article, fixing something that looks difficult at first, is pretty easy to do using standard ASP.NET practices, like using FindControl.

Download Files

Source Code for this Article

Where to Next?

Wonder where to go next? You can read existing comments below or you can post a comment yourself on this article .


Consider making a donation
Please consider making a donation using PayPal. Your donation helps me to pay the bills so I can keep running Imar.Spaanjaars.Com, providing fresh content as often as possible.



Feedback by Other Visitors of Imar.Spaanjaars.Com

On Wednesday, March 07, 2007 4:27:11 AM Agachart said:
This is good site
On Thursday, October 11, 2007 8:49:33 AM Swapnil Mantri said:
Hi, this is given example is working fine.Please give me the another link for business logic .

Thanks & Regards,
Swapnil Mantri
On Monday, March 17, 2008 9:28:00 PM Atom said:
If you look at the markup generated by this control though it's horribly heavy!
a table with tonnes and tonnes of inline styling code.
On Monday, March 17, 2008 10:18:48 PM Imar Spaanjaars said:
Hi there,

What control are you talking about? The ValidatorCallout doesn't render as a table.

If you are talking about the Login control (which is not the main topic of this article), you may want to check out the CSS Control Adapters Toolkit located here: http://www.asp.net/cssadapters/ With this toolkit, you can render a clean layout instead of relying on tables.

The markup generated by the control depends largely on your own implementation. Look at the live demo page and you'll see exactly one style attribute for a table, and two for the validators. If you manually set all the styles on the control, you do indeed end up with a lot of in-line attributes. As will most ASP.NET controls, you're much better off using the CssClass attribute and control the looks through an external CSS file.

Cheers,

Imar
On Thursday, March 20, 2008 6:09:04 AM Atom said:
Sure it does... in all its LemonChiffon glory. Check it out in Firebug if you don't believe me.  Problem with this is I apply default formatting like padding and text-align to all tables in css so this control doesn't work

<snip>Code cut by Imar</snip>
On Thursday, March 20, 2008 6:33:33 AM Imar Spaanjaars said:
Hi Atom,

It could be me, but I don't see what you are seeing on this page, even with Firefox:

http://imar.spaanjaars.com/Demos/ValidatorCalloutExtender/

Maybe you are talking about your own page where you have applied other styling info to the controls, rather than relying on CssClass? Have you looked at the CSS Adapters?

Please note, I wrote this piece of code to show you how to integrate the ValidatorCallout with templated ASP.NET controls, not to highlight its clean XHTML syntax. Maybe you are barking up the wrong tree?

Imar
On Thursday, March 20, 2008 10:12:37 PM Atom said:
I would've liked to check out http://imar.spaanjaars.com/Demos/ValidatorCalloutExtender/ but it's behind authentication.

:-(

Don't get me wrong, I hope I'm wrong... but i only noticed it when I tried to use it on a styled page.  I think the problems I came across were padding on table and text-align:left.  When I disabled those properties the control worked well.

I really do like the CSS Control Adapters too, by the way.  I use them all the time.

Usually I always use CSSClass... (I HATE inline styling... it's just so messy)  With the Callout extender I left it all default.
On Thursday, March 20, 2008 11:34:04 PM Imar Spaanjaars said:
That page is *not* behind authentication; it's the page with the authentication itself that is the demo.

Just leave all fields blank and click the Login button to see the extenders....

Imar
On Thursday, March 20, 2008 11:59:48 PM Atom said:
LOL! I didn't even think to check!!!  I just assumed it was authenticated.  Though you may want to try a page that doesn't look like a login screen (thought i see that you used it in your demo)

Anyway, I checked it out.  It renders as a table there, along with 10 nested divs each smaller than the other (Making the pointy thing on the left)

NOTE: It won't show up if you view source.
On Friday, March 21, 2008 12:07:31 AM Imar Spaanjaars said:
Quote from the article:

"take a look at this live demo. Leave the User Name and Password fields blank and click the Log In button. You'll see the following screen appear"

Things don't get any clearer than that.... ;-)

Much of the behavior is caused by dynamic JavaScript; stuff that gets added to the DOM when the page loads, or when the extenders are triggered. Those are indeed hard to style, so you may need external CSS to override inline styling if required. Alternatively, you could change the behavior of the extender control or write your own...

Imar
On Monday, March 24, 2008 9:48:51 AM Chandra said:
Hi All,
I am facing a problem the ValidatorCallout control.
The place where the message should be displayed (next to the control) is not working properly when we restore or resize the window. can any one give me a work around to resolve this problem.

Thanks in advance
On Monday, March 24, 2008 9:58:18 AM Imar Spaanjaars said:
Hi Chandra,

I think you're better off posting this at the appropriate forum over at the official ASP.NET site: www.asp.net

Cheers,

Imar
On Wednesday, November 05, 2008 4:07:50 AM Ajaxlearner said:
How to apply CSS on Ajax ValidatorCalloutEntender?
I want to change the look n feel for Callout popup.

On Wednesday, November 05, 2008 9:13:10 PM Imar Spaanjaars said:
Hi Ajaxlearner,

Did you look at the control's documentation page? http://www.asp.net/ajax/ajaxcontroltoolkit/samples/ValidatorCallout/ValidatorCallout.aspx

It has a CssClass property....

Cheers,

Imar
On Wednesday, November 10, 2010 5:10:46 AM piyush jain said:
Hello,
I want to change the color of this " Validator Callout Extender " . which is yellow as default.
so is it possible to do it.
On Wednesday, November 10, 2010 6:31:51 AM Imar Spaanjaars said:
Hi piyush,

Check out my answer to Ajaxlearner, right above your post.

Cheers,

Imar
On Sunday, May 08, 2011 6:01:13 AM Mads said:
This is just what I needed. Some help to extend the use of MS brilliant controls. Keep up the good work!
On Tuesday, January 03, 2012 3:24:36 PM Ankit Singh said:
Very informative post its really helpful for me and helped me to complete my task. Thanks for sharing with us. I have found another nice post with wonderful explanation on Ajax Toolkit ValidatorCalloutExtender Control in ASP.Net, for more details of that post please check out this link...

http://mindstick.com/Articles/086d85d8-8721-4689-b950-5347b511738f/?Ajax%20Toolkit%20ValidatorCalloutExtender%20Control%20in%20ASP.Net

Thanks

Talk Back! Comment on Imar.Spaanjaars.Com

I am interested in what you have to say about this article. Feel free to post any comments, remarks or questions you may have about this article. The Talk Back feature is not meant for technical questions that are not directly related to this article. So, a post like "Hey, can you tell me how I can upload files to a MySQL database in PHP?" is likely to be removed. Also spam and unrealistic job offers will be deleted immediately.

When you post a comment, you have to provide your name and the comment. Your e-mail address is optional and you only need to provide it if you want me to contact you. It will not be displayed along with your comment. I got sick and tired of the comment spam I was receiving, so I have protected this page with a simple calculation exercise. This means that if you want to leave a comment, you'll need to complete the calculation before you hit the Post Comment button.

If you want to object to a comment made by another visitor, be sure to contact me and I'll look into it ASAP. Don't forget to mention the page link, or the QuickDocId of the document.

For more information about the Talk Back feature, check out this news item.