Default Focus, Buttons and Validation Errors with ASP.NET 2.0
Default Buttons in ASP.NET 2.0
One of the common requests with ASP.NET 1.1 today is to have better control over what happens when a user in a browser hits the enter key without having a button or post-back control selected.
Browsers often do weird things when this happens -- and can cause a form submit to occur without sending any action element for the server to figure out what action was supposed to have happened. This was apparently a browser bug in one of the early Netscape 1.x builds, and IE 3.x copied it to stay compatible. Today it remains an annoying artifact that can be hard to work around.
ASP.NET 2.0 makes this easier by supporting the concept of a "default button" that can be indicated on either a <form> or <asp:panel> container control. If no button is selected at the time the enter key is selected, the defaultbutton property will drive the appropriate post-back to the server and route the message to the control you want.
The actual control picked is context specific based on where the cursor currently is and what <asp:panel> or <form> container control it is nested within.
So for example, with the below sample:
<html>
<body>
<form defaultbutton=“button1” runat=“server”>
<asp:button id=“button1” text=“Same Page” runat=“server”/>
<asp:panel defaultbutton=“button2” runat=“server”>
<asp:textbox id=“foo” runat=“server”/>
<asp:button id=“button2” runat=“server”/>
</asp:panel>
</form>
</body>
</html>
If the enter key is selected the first time the page is loaded, "button1" will be the button that receives the post-back event. If the enter key is hit while the user has their cursor focus within the "foo" textbox (contained wtihin the <asp:panel>), then "button2" will be the button that receives the post-back event.
You can nest these relationships arbitrarily deep, and they provide a clean declarative way to control this functionality. You can also control this programmatically with the container APIs.
Default Focus in ASP.NET 2.0
One of the other common requests with ASP.NET 1.1 today is to have better server control over what control has the focus event in the browser when a page is loaded. Today you have to write your own javascript to enable this.
With ASP.NET 2.0 you can now also set a "defautfocus" property on the <form> tag to control this as well. You can likewise set this programmatically using the Page.SetFocus(control) method where you pass in a reference to a control, or by calling a "Focus()" method now surfaces on ASP.NET TextBox and other input controls on the server.
For example, the below sample will cause TextBox2 to have focus when the page first loads:
<html>
<body>
<form defaultfocus=“textbox2” defaultbutton=“button1” runat=“server”>
<asp:textbox id=“textbox1” runat=“server”/>
<asp:textbox id=“textbox2” runat=“server”/>
<asp:button id=“button1” text=“Same Page” runat=“server”/>
<asp:panel defaultbutton=“button2” runat=“server”>
<asp:textbox id=“foo” runat=“server”/>
<asp:button id=“button2” runat=“server”/>
</asp:panel>
</form>
</body>
</html>
Focus on Errors
One of the other common "focus" requests has been to enable validation controls in ASP.NET to manage control focus when a validation error occurs. This enables users to quickly (without having to touch a mouse) fix up errors and try to continue.
With ASP.NET 2.0, there is now a "SetFocusOnError" property on all validation controls which makes this easy. If you set it to true, and an error occurs, the control the validator is attached to will have focus immediately with no more developer action required.
For example:
<asp:TextBox ID="TextBox3“ runat="server“ />
<asp:RequiredFieldValidator
SetFocusOnError="true"
ErrorMessage="TextBox3 is empty" ControlToValidate="TextBox3"
runat="server“/>
Note that validators also now work client-side for all recent browsers (so not just IE anymore -- also Firefox, etc), in addition to working on the server.
Putting it all together
To see how all of the above feature can work together, try saving and running the below sample. It sets the focus of the page to the second text box when first loaded. Enter some data in the first textbox (but not the other two), and hit enter. Notice the validators fire, and your focus is automatically set to the first one with an error (no tabbing or mouse movement required). Enter data in this one and hit enter, notice the last validator fires and your focus is now there. Enter data and hit enter and you are clean -- and the Button1 button will send a postback event to the server.
Then expand the hidden panel and enter data in the textbox within it. Without moving your cursor from that textbox hit enter, and notice that the button2_click event fires -- because it is defined to be the default button for that scope.
------------------------------------------------
PageFocus.aspx
------------------------------------------------
<%@ Page Language="VB" AutoEventWireup="false" CodeFile="Pagefocus.aspx.vb" Inherits="Pagefocus" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
<title>Focus API, DefaultButton, DefaultFocus, and SetFocusOnError</title>
</head>
<body>
<form id="form1" DefaultButton="Button1" DefaultFocus="TextBox2" runat="server">
<div>
<h3>Focus API, DefaultButton, DefaultFocus, and SetFocusOnError</h3>
TextBox 1:
<asp:TextBox ID="TextBox1" runat="server"></asp:TextBox>
<asp:RequiredFieldValidator SetFocusOnError="true" ErrorMessage="TextBox1 is empty"
ID="RequiredFieldValidator1" ControlToValidate="TextBox1" Display="Dynamic" runat="server">*</asp:RequiredFieldValidator>
<br />
TextBox 2:
<asp:TextBox ID="TextBox2" runat="server"></asp:TextBox>
<asp:RequiredFieldValidator SetFocusOnError="true" ErrorMessage="TextBox2 is empty"
ID="RequiredFieldValidator2" ControlToValidate="TextBox2" Display="Dynamic" runat="server">*</asp:RequiredFieldValidator>
<-- notice the cursor starts here
<br />
TextBox 3:
<asp:TextBox ID="TextBox3" runat="server"></asp:TextBox>
<asp:RequiredFieldValidator SetFocusOnError="true" ErrorMessage="TextBox3 is empty"
ID="RequiredFieldValidator3" ControlToValidate="TextBox3" Display="Dynamic" runat="server">*</asp:RequiredFieldValidator>
<br />
<br />
<asp:Button ID="Button1" runat="server" Text="Submit" />
<br />
<br />
<asp:ValidationSummary ID="ValidationSummary1" runat="server" />
<asp:LinkButton ID="LinkButton1" CausesValidation="false" runat="server">Show Panel</asp:LinkButton><br />
<br />
<asp:Panel Visible="false" DefaultButton="Button2" ID="Panel1" runat="server" Width="125px">
Enter Your Name:
<asp:TextBox ID="TextBox4" runat="server"></asp:TextBox><br />
<br />
<asp:Button ID="Button2" CausesValidation="false" runat="server" Text="Go" />
<br />
<br />
<asp:Label ID="Label1" runat="server"></asp:Label><br />
</asp:Panel>
</div>
</form>
</body>
</html>
------------------------------------------------
PageFocus.aspx.vb
------------------------------------------------
Partial Class Pagefocus
Inherits System.Web.UI.Page
Sub LinkButton1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles LinkButton1.Click
If Not Panel1.Visible Then
Panel1.Visible = True
LinkButton1.Text = "Hide Panel"
TextBox4.Focus()
Else
Panel1.Visible = False
LinkButton1.Text = "Show Panel"
Page.SetFocus(TextBox1)
End If
End Sub
Sub Button2_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles Button2.Click
Label1.Text = "Hello " & TextBox4.Text
End Sub
End Class