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>
      &lt;-- 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

28 Comments

  • Scott,



    Lookling forward to the changes. One of the tools I've used with ASP.NET 1.1 is a control called DefaultButtons from MetaBuilders. What I like about this control is that if I put it on my page, it adds a property to my TextBoxes and other input controls called DefaultButton. I can then specify which button will be clicked if the user presses the Enter key while that control has focus. This seems a bit different (and a bit more functional) than the functionality in ASP.NET 2.0. I'd rather not have to wrap individual TextBoxes in a Panel for the type of functionality I'm talking about.



    Am I missing something in the functionality of ASP.NET 2.0 that can handle this scenario? Basically, I'm looking to declaritively establish which button event will be triggered for each TextBox on a page when the user taps the Enter key.



    Thanks, as always, for all of the great work you and the VS team do.

  • the asp:Panel has a DefaultButton property. either wrap your textbox in the a panel ( ugly ) or use reflector to inspect the code and subclass your own textbox.



    All the javascripts the Panel uses for doing this are present as webresources. :-)

  • Your team is doing great job by addressing these issues. I will like to share a problem with u and expect a response from u earlier. I had developed a web hosting shopping cart system using ASP.Net 2.0. I used ASP.Net 2.0 validation controls in serveral web pages. The validation works fine in most browsers on my local system. I deployed this application on a server it ran very fine. Then i need to switch server, now what happended is that validation do not work at all, not at client side nor on server side. I have searched google for this problem. The solutions found was to use aspnet_reg and redeploy application using another folder. But nothing worked in my case. I could not figure out what might have gone wrong. I have checked every possible options (checked asp.net version application is using, aspnet_client folder etc). &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; If you know any solution then please help me out. Thanks usmankah@yahoo.com

  • Hi Muhammad,

    I'm not sure what you are running into there. If you want to send me email with more details (scottgu@microsoft.com) I can try to help.

    Hope this helps,

    Scott

  • I think it's broken. I've been preparing an asp.net 2.0 training course and wanted to show off the default button functionality with panels, and the validation group and setfocus on error stuff as well.

    Stick this in a page. Then, click in the top text box, hit enter. You get the top form validation error as expected. Then, click in the bottom text box, hit enter. It tries to submit the top form!?

    If you load the page again from a new browser and click in the bottom form and then hit enter, the bottom form tries to submit as expected.

    Is this a bug?





    Top Form







    test



    Bottom Form






  • Hi Keith,

    Can you send me email (scottgu@microsoft.com) with the code-sample you are trying as well as the exact repro steps? I can then look at it and figure out what is going on.

    Thanks,

    Scott

  • Hi Gary,

    From within your content page you should be able to write code like this to set the default button:

    Page.Form.DefaultButton = "MyButton"

    The trick is going to be to make sure you specify the right name for the button. Rather than use the local name to the content-page, I think you need to specify the unique name. Try something like this:

    Page.Form.DefaultButton = MyButton.UniqueID;

    If that doesn't work, send me email (scottgu@microsoft.com) and I'll work up a sample for you that does (it will also make a good tip/trick to blog about ).

    Thanks,

    Scott

  • Did this work? I have the same issue

  • Ty,

    Using this:
    Page.Form.DefaultButton = MyButton.UniqueID;

    Worked for me.

    --Michael

  • Is there anyway to turn off the default focus in the content page? Cause content page with the same master page but do not want the defualt focus.

    thank

  • Hi Cedar,

    You can call the Page.SetFocus() method to cause a particular control to have focus when the page renders.

    Hope this helps,

    Scott

  • About Defaultbutton: Great just what I needed

  • Hi Scott,

    Default button is working fine if the button inside the page.

    try this scenario,
    you have the page which has a master page and you put default property on master page form tag,and if you put the full Cleint ID for button is shows this error :

    The DefaultButton of 'form1' must be the ID of a control of type IButtonControl.

    how can you sove this problem ?

  • Hi Problem,

    What you'll need to-do is to fully qualify the path of the button in order to have it work within the content section of the master-page.

    Hope this helps,

    Scott

  • Hi Scott,

    I have one question. I don't want a default button on the page. Means pressing Enter Key should not fire any event. How is it possible?

    Gokul Tyagi

  • How can I set the default button to a button inside a Login Control on a login page, or a button iside a Create User Wizard Control on a registration page, etc.? I don't know how to access them.
    Thanks!

  • Bit of a problem with this, to be honest.

    If you have two forms with validation controls on them, upon any kind of validation event with no page transition the page promptly 'forgets' the defaultbutton property.

    So on a page with two panels with different defaultbuttons, if the input into the second panelis correct on the first try it works. If you fail to enter a required field press enter again, instead of using the second panel it instead uses the first.

    Am I missing something obvious here?

    Thanks in advance.

  • On the heels of my last post, a little more rooting around found the answer :

    function Page_ClientValidate(validationGroup) {
    Page_InvalidControlToBeFocused = null;
    if (typeof(Page_Validators) == "undefined") {
    return true;
    }
    var i;
    for (i = 0; i < Page_Validators.length; i++) {
    ValidatorValidate(Page_Validators[i], validationGroup, null);
    }
    ValidatorUpdateIsValid();
    ValidationSummaryOnSubmit(validationGroup);
    Page_BlockSubmit = !Page_IsValid;
    if(!Page_IsValid)
    __defaultFired = false;
    return Page_IsValid;
    }

    Popping that at the bottom of my master page (well, in a script file) sorted it out.

    I guess this has been reported to you guys already. Interestingly, the defaultbutton/validation bug does not occur on FireFox. No comment there ;)

    Cheers

  • Hi Tom,

    Any chance you could send me an email (scottgu@microsoft.com) with a small repro for the issue you are seeing?

    I'll then loop in a few folks to the thread who can help investigate it.

    Thanks,

    Scott

  • Hi Scott,

    I am facing a strange issue. I am using DefaultButton inside Panel control and it is assinged as DefaultButton="btnAddToCart". This DefaultButton functionality works like charm on my development PC (winxp sp 2 and it works with ie7 & firefox) also this page is referencing Master page. But when I moved this website into production (live) this doesn’t work which is basically on a hosting company server.

    Do think is there any thing specially on server enviourment it is causing the issue and is there any thing which I can tell the hosting company to enable to get start work this DefaultButton?

    Even I put the setup another winxp sp2 pc and it worked as well. i think something causing from the server enviourment or the security settings of the hosting server.

    What you think about this behavious?

    thanks
    Thomas

  • Hi Scott,

    I am facing a strange issue. I am using DefaultButton inside Panel control and it is assinged as DefaultButton="btnAddToCart". This DefaultButton functionality works like charm on my development PC (winxp sp 2 and it works with ie7 & firefox) also this page is referencing Master page. But when I moved this website into production (live) this doesn’t work which is basically on a hosting company server.

    Do think is there any thing specially on server enviourment it is causing the issue and is there any thing which I can tell the hosting company to enable to get start work this DefaultButton?

    Even I put the setup another winxp sp2 pc and it worked as well. i think something causing from the server enviourment or the security settings of the hosting server.

    What you think about this behavious?

    thanks
    thomas

  • Hi Thomas,

    That is really odd - I haven't heard of an issue like this before. Can you do a view-source in the returned pages to see if there are any markup differences?

    Thanks,

    Scott

  • I am having problems similar to Tom Newman's initial issue. I have multiple texboxes that should fire a specific default button when Enter is pressed while one is focused. The first time one of them is clicked, it works correctly, but as soon as one "higher" on the page is clicked, the rest will only fire the defaultButton of the top panel.

    I.E. If I enter a SSN in the SSN texbox and press enter it fires correctly. Then if I do the same for the Employee ID, it fires correctly. If I then do FirstName, LastName or SSN again it fires the Employee ID Button.

    I am obviously filtering each button click through the same method and switching on the commandName. I can't see where that would be a problem.

    Any Ideas?

    ----- CODE -----






































    -----

  • I found out what was causing this issue. I am working in an "ajaxified" environment, and if I disable ajax it works as intended. I'm assuming the bug is in the client losing which control has focus after a callback is performed, so on a subsequent Enter-Press, it fires the first button on the page...

    Might be able to come up with a Javascript work-around, but not sure....

  • I thought I would follow-up and post my "solution" for how to reference controls within the 2.0 Login controls. View Source in IE was the only way I could figure out how to correctly reference these controls. Maybe I'm missing something in VS2005 but you certainly don't reference them by what is in the designer page. Does anyone know of a better way besides View Source?

    Someone above asked how to reference this stuff... so here's what I did:

    Login Control:
    Page.Form.DefaultFocus = Login1.FindControl("UserName").UniqueID;
    Page.Form.DefaultButton = Login1.FindControl("LoginButton").UniqueID;

    Password Recovery Control:
    Page.Form.DefaultFocus = PasswordRecovery1.FindControl("UserNameContainerID$UserName").UniqueID;
    Page.Form.DefaultButton = PasswordRecovery1.FindControl("UserNameContainerID$SubmitButton").UniqueID;

    Create User Wizard:
    Page.Form.DefaultFocus = CreateUserWizard1.FindControl("CreateUserStepContainer$UserName").UniqueID;
    The default button seems to be already set on the Create User Wizard.

  • Hi,

    The asp:panel defaultbutton is great, but it does not work if the referenced button is outside the panel...

    Regards

  • Thanks Scott! This is precisely what I have been looking for. For days I have been trying to fix this DefaultButton focus behavior, and this fixes it!

  • The panel has no effect on a LoginView. So you can't have a Login + any other own form on one site with seperated enter-handling. (Loginview-Enter will always fire the other form.)

Comments have been disabled for this content.