:::: MENU ::::

Friday, February 20, 2009

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.