Software Geek

March 23, 2008

A Quick Fix for the Validator SetFocusOnError Bug

Filed under: Software
The ASP.NET validators have this nice property called “SetFocusOnError” that is supposed to set the focus to the first control that failed validation. This all works great until your validator control is inside a naming container. I ran into this recently when using validators in a DetailsView. Take this simple example:
<%@ Page Language=”C#” %>
<script runat=”server”>
 protected void Page_Load(object sender, EventArgs e)
 {
 if (!IsPostBack)
 DataBind();
 }
</script>
<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN” “http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”>
<html xmlns=”http://www.w3.org/1999/xhtml”>
<head runat=”server”>
 <title></title>
</head>
<body>
<form id=”_frm” runat=”server”>
 <asp:DetailsView
 ID=”dv1″
 DefaultMode=”Edit”
 DataSource=’<%# new object[1] %>’
 runat=”server”
 >
 <Fields>
 <asp:TemplateField HeaderText=”First Name:”>
 <EditItemTemplate>
 <asp:TextBox ID=”FirstNameTextBox” runat=”server” />
 <asp:RequiredFieldValidator
 ID=”FirstNameValidator1″
 ControlToValidate=”FirstNameTextBox”
 ErrorMessage=”First name is required.”
 Display=”Dynamic”
 EnableClientScript=”false”
 SetFocusOnError=”true”
 ValidationGroup=”bug”
 Text=”*”
 runat=”server”
 />
 </EditItemTemplate>
 </asp:TemplateField>
 </Fields>
 <FooterTemplate>
 <asp:ValidationSummary
 ID=”vs1″
 DisplayMode=”List”
 ValidationGroup=”bug”
 runat=”server”
 />
 <asp:Button
 ID=”Button1″
 Text=”Post Back”
 ValidationGroup=”bug”
 runat=”server”
 />
 </FooterTemplate>
 </asp:DetailsView>
</form>
</body>
</html>

If you run this page and do a view source you’ll see that the FirstNameTextBox gets rendered like this:

Live Support Server: Jerry Messenger is Jabber/XMPP based Live Support Solution for your websites.
<input name=”dv1$FirstNameTextBox” type=”text” id=”dv1_FirstNameTextBox” />
If you just do a post back without entering a value to cause the validator to fail it will output this line of java script in an attempt to set the focus to the invalid element:

Also see: The Internet is Officially Dead & Boring - Its the economy stupid !

Also see: Bloggers in the Mavs Locker Room ?

Also see: The influence of style upon methodology…

Also see: Quaker votes

Also see: Updated Finalization and Hosting

Also see: C# 3.0 Lambdas and Type Inference

Also see: Access to old blogs

Also see: Chicago geek dinner 11/22

Also see: DevWeek 2008 Cross Platform Silverlight Demos

Also see: Hello world!

Also see: TransparentProxy

Also see: REST2SQL in a Jiffy, with Tagspace for Spice

Also see: Sliced Bananas On Opaque Data

Also see: A quick update on me.

Also see: Chicago geek dinner 11/22

Also see: My Presidential Endorsement:

Also see: VPC 2007 Dual Monitor support

Also see: LoadFile vs. LoadFrom

Also see: TransparentProxy

Also see: Dare Obasanjo on C# Anonymous Types

Also see: Finally, the Killer App

Also see: LoadFrom’s Second Bind

Also see: Prototypes and Java Config with Spring

Also see: Snippet Compiler update

Also see: I’ve finally settled into my new position on the Internet Explorer team…

Also see: Java perfomance talk

Also see: ASP.NET MVC in CodePlex and Extensible Unit Testing

Also see: Spring Web Flow features and feedback request

Also see: C# 3.0 Lambdas and Type Inference

Also see: Generics and .NET

Also see: REST2SQL in a Jiffy, with Tagspace for Spice

Also see: App.config Examples

Also see: Sliced Bananas On Opaque Data

WebForm_AutoFocus(‘FirstNameTextBox’);

See anything wrong with this? It would seem that the validators just use the string value you typed in for the ControlToValidate property rather than doing a FindControl and using the UniqueID. This is exactly what happens and I verified it with reflector. The Validate method on BaseValidator does this:
if ((!this.IsValid && (this.Page != null)) && this.SetFocusOnError)
{
 this.Page.SetValidatorInvalidControlFocus(this.ControlToValidate);
}

If you follow the call to SetValidatorInvalidControlFocus you’ll see that it never resolves the full UniqueID of the control that its going to set focus to.
 
Ok, so this sucks. How do I work around it. My solution was to simply ditch using the SetFocusOnError property and implement the focus logic myself which is actually pretty easy. I overrode Validate method on my Page like this:
Multisoft Group: Custom software solutions for your business.

Also see: VPC 2007 Dual Monitor support

Also see: A quick update on me.

Also see: Hello world!

Also see: Generating WPF Content with LINQ

Also see: C# 3.0 Lambdas and Type Inference

Also see: A web site is not an RSS feed…nor the reverse.

Also see: Using IronPython for Dynamic Expressions.

Also see: Prototypes and Java Config with Spring

Also see: Memory Model

Also see: TransparentProxy

Also see: TransparentProxy

Also see: TransparentProxy

Also see: Scott Guthrie presents at NDDNUG

Also see: NHL seven days a week

Also see: LoadFrom’s Second Bind

Also see: Java Concurrency, another series on its issues

Also see: Chicago geek dinner 11/22

Also see: Language parsing and compiler design doesn’t have to be hard, but boy this book really sucks!

Also see: C# 3.0 Lambdas and Type Inference

Also see: On the Perils of Wikipedia

Also see: DevWeek 2008 Cross Platform Silverlight Demos

Also see: Silverlight 2 Developer Poster

Also see: Spring Web Flow features and feedback request

Also see: A web site is not an RSS feed…nor the reverse.

Also see: Resizing a Form has always been a pain in the rectum…

public override void Validate(string group)
{
 base.Validate(group);
	
 // find the first validator that failed
 foreach (IValidator validator in GetValidators(group))
 {
 if (validator is BaseValidator && !validator.IsValid)
 {
 BaseValidator bv = (BaseValidator)validator;
	
 // look up the control that failed validation
 Control target =
 bv.NamingContainer.FindControl(bv.ControlToValidate);
	
 // set the focus to it
 if (target != null)
 target.Focus();
	
 break;
 }
 }
}
If your using C# 3 this is even easier using LINQ:
public override void Validate(string group)
{
 base.Validate(group);
	
 // get the first validator that failed
 var validator = GetValidators(group)
.OfType<BaseValidator>()
.FirstOrDefault(v => !v.IsValid);
	
 // set the focus to the control
 // that the validator targets
 if (validator != null)
 {
 Control target = validator
.NamingContainer
.FindControl(validator.ControlToValidate);
	
 if (target != null)
 target.Focus();
 }
}

I hope this saves someone the headache of tracking this down.

http://weblogs.asp.net/dfindley/archive/2007/06/29/a-quick-fix-for-the-validator-setfocusonerror-bug.aspx

Comments »

The URI to TrackBack this entry is: http://annil12.blogsome.com/2008/03/23/a-quick-fix-for-the-validator-setfocusonerror-bug-2/trackback/

No comments yet.

RSS feed for comments on this post.

Leave a comment

Line and paragraph breaks automatic, e-mail address never displayed, HTML allowed: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <code> <em> <i> <strike> <strong>



Anti-spam measure: please retype the above text into the box provided.

Get free blog up and running in minutes with Blogsome
Theme designed by Jay of onefinejay.com