Create new account I forgot my password    

Shift Focus to the Next Control during a PostBack using ASP.NET and LINQ
Rating: 14 user(s) have rated this article Average rating: 4.7
Posted by: Suprotim Agarwal, on 1/5/2009, in category "ASP.NET 2.0 & 3.5"
Views: this article has been read 22540 times
Abstract: We at times come across a requirement where a feature in a web application is supposed to behave similar to that of its windows counterpart. One such requirement is to either maintain the focus on the control that caused a postback or to shift focus to the next control after a postback. In this article, we will use ASP.NET and LINQ to do so.

Shift Focus to the Next Control during a PostBack using ASP.NET and LINQ
 
We at times come across a requirement where a feature in a web application is supposed to behave similar to that of its windows counterpart. One such requirement is to either maintain the focus on the control that caused a postback or to shift focus to the next control after a postback.
Ryan Farley has two cool articles on Determining the Control that Caused a PostBack  and Set Focus to an ASP.NET Control. We will make use of his code to determine the control that caused a postback. We will then build on that code and use LINQ to loop through the controls, find the TabIndex of the control that caused postback and then shift focus to the control having the next TabIndex. I got this idea of using LINQ while reading a forum post and thought that this solution would be worth sharing with others.
In an application where a lot of text fields are involved, we generally see that when a user tabs out of a textbox, some calculation is performed and the calculated value is then displayed back in the textbox. On a page not powered with ASP.NET AJAX, the desired behavior is to display the calculated value in the textbox and shift the focus to the next one.
In an ASP.NET page, the __doPostBack is used by server controls to cause a postback. If you observe the html markup after a postback, ASP.NET automatically adds two hidden fields (“__EVENTTARGET” and “__EVENTARGUMENT”) and a client-side script method (“__doPostBack”) to the page. The EVENTTARGET is the ID of the control that caused the postback and the EVENTARGUMENT contains any arguments passed that can be accessed on the server. The __doPostBack method sets the values of the hidden fields and causes the form to be submitted to the server. Ryan makes use of the __EVENTTARGET to find the control that caused postback.
Note: Remember that Button and the ImageButton do not use the __doPostBack unless the UseSubmitBehaviour property is set explicitly.
In this sample of ours, I am using a couple of controls on the form to test out our logic. There are a few TextBoxes with AutoPostBack = true which will cause a postback whenever the control looses focus. I also have a couple of Buttons and ImageButton on the page. We will set the TabIndex of each of these controls as shown below:
The markup looks similar to the following:
   <form id="form1" runat="server">
    <div>
        <asp:TextBox ID="TextBox1" runat="server" AutoPostBack="True" TabIndex="1">
        </asp:TextBox>
        <br />
        <asp:TextBox ID="TextBox2" runat="server" AutoPostBack="True" TabIndex="2">
        </asp:TextBox>
        <br />
        <asp:Button ID="Button1" runat="server" Text="Button" TabIndex="3" />
        <br />
        <asp:Button ID="Button2" runat="server" Text="Button" TabIndex="4" />       
        <br />
    </div>
    </form>
The code to set focus to the next control after a postback is given below:
C#
    protected void Page_Load(object sender, EventArgs e)
    {
        if (Page.IsPostBack)
        {
            WebControl wcICausedPostBack = (WebControl)GetControlThatCausedPostBack(sender as Page); 
            int indx = wcICausedPostBack.TabIndex;                      
            var ctrl = from control in wcICausedPostBack.Parent.Controls.OfType<WebControl>()
                       where control.TabIndex > indx
                       select control;
            ctrl.DefaultIfEmpty(wcICausedPostBack).First().Focus();
        }
    }
 
    protected Control GetControlThatCausedPostBack(Page page)
    {
        Control control = null;
 
        string ctrlname = page.Request.Params.Get("__EVENTTARGET");
        if (ctrlname != null && ctrlname != string.Empty)
        {
            control = page.FindControl(ctrlname);
        }
        else
        {
            foreach (string ctl in page.Request.Form)
            {
                Control c = page.FindControl(ctl);
                if (c is System.Web.UI.WebControls.Button || c is System.Web.UI.WebControls.ImageButton)
                {
                    control = c;
                    break;
                }
            }
        }
        return control;
 
    }  

VB.NET
   Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
        If Page.IsPostBack Then
            Dim wcICausedPostBack As WebControl = CType(GetControlThatCausedPostBack(TryCast(sender, Page)), WebControl)
            Dim indx As Integer = wcICausedPostBack.TabIndex
            Dim ctrl = _
             From control In wcICausedPostBack.Parent.Controls.OfType(Of WebControl)() _
             Where control.TabIndex > indx _
             Select control
            ctrl.DefaultIfEmpty(wcICausedPostBack).First().Focus()
        End If
    End Sub
 
    Protected Function GetControlThatCausedPostBack(ByVal page As Page) As Control
        Dim control As Control = Nothing
 
        Dim ctrlname As String = page.Request.Params.Get("__EVENTTARGET")
        If ctrlname IsNot Nothing AndAlso ctrlname <> String.Empty Then
            control = page.FindControl(ctrlname)
        Else
            For Each ctl As String In page.Request.Form
                Dim c As Control = page.FindControl(ctl)
                If TypeOf c Is System.Web.UI.WebControls.Button OrElse TypeOf c Is System.Web.UI.WebControls.ImageButton Then
                    control = c
                    Exit For
                End If
            Next ctl
        End If
        Return control
 
    End Function
The code makes use of the GetControlThatCausedPostBack function written by Ryan to find the control that caused the postback. We then determine the TabIndex of the control and use LINQ to select the control with the next tabindex and set focus to it .I have used LINQ as I find it very useful when I loop over collections. It gives me all the control I need over the code, keeping it tight and without much effort.
Just run the application and test out the functionality. When you tab out of a TextBox or click on the Button control, the focus shifts to the next control after a postback. That’s it for now. I hope you liked the article and I thank you for viewing it. 
 If you liked the article,  Subscribe to my RSS Feed or Subscribe Via Email









Page copy protected against web site content infringement by Copyscape


How would you rate this article?

User Feedback
Comment posted by swarna on Monday, January 12, 2009 11:10 PM
hello
Comment posted by Kirti Darji on Tuesday, April 28, 2009 6:18 AM
when i use in gridview foter after added two row focus are Maintain on foter Textbox but after that not Focus on Foter Text Box Please Provide help
Comment posted by Suprotim Agarwal on Wednesday, April 29, 2009 8:03 AM
Kirti: Can you mail me the code using the Contact link at the top of this page.
Comment posted by Jonathan Small on Thursday, March 25, 2010 9:11 AM
I have an asp.net page where I dynamically generate a series of text boxes.  When generated, I generate an ID of txtNewElection_NN where NN is a number from 01 through some other number.  I also generate a tab index starting at 1 and bumping by 1 for each dynamically generated text box.  All text boxes have autopostback set to true.  When I populate txtNewElection_02 and press the tab key, a postback is executed.  I execute your code.  In the function GetControlThatCausedPostBack, the variable ctrlname gets the value of txtNewElection_02 correctly.  However, the line control = page.FindControl(ctrlname) results in control being nothing.  Could this be because my textbox fields are being generated dynamically?  The only thing I could think of would be that the dynamically generated textboxes don't have runat="server" (or do they?).  Any help would be greatly appreciated.
Comment posted by Kasper on Friday, March 26, 2010 6:21 AM
Jonathan, if you build the controls before you execute the code, you should be able to find them using findcontrol.

Post your comment
Name:
E-mail: (Will not be displayed)
Comment:
Insert Cancel

NEWSLETTER