Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ASP.NET postback from wrong dropdownlist after using browser Back button

I've got an ASP.NET 2.0 web application that is partly navigated by using DropDownList controls. When one of them is changed, it causes a postback, which is then redirected to another URL depending on what was chosen in the dropdown.

I've noticed strange behavior when using the browser's Back button after using the dropdowns. The procedure is this:

  1. Make a selection in one of the dropdowns, causing a postback and redirect. Good so far.
  2. Click the browser's Back button.
  3. In another navigation dropdown below the one used before (they're all contained in one div), make a selection. The page redirects to the same URL as the first time, not what it should redirect to based on this other dropdown.

I've tried this in both Firefox 10 and IE9 and seen the same thing. I looked at the Net tab in Firebug and saw that in the POST for step 3, the correct control is referenced. But when I debug it, the event handler for the wrong dropdown (the one used in step 1) fires.

The code is pretty simple and straightforward. Example of markup:

<asp:DropDownList runat="server" ID="ddlTest" AutoPostBack="true" />

The dropdowns actually aren't plain <asp:DropDownList ... /> elements; I'm inserting optgroup elements with an approach similar to this.

Example of C#:

ddlTest.Click += new EventHandler(ddlTest_SelectedIndexChanged);

And in ddlTest_SelectedIndexChanged:

if (ddlTest.SelectedValue != "")
{
   Response.Redirect(MyUtilClass.GetUrl(ddlTest.SelectedValue));
}

What's going on here?

UPDATE 2/6/2012: I've fixed this by checking for the content of Request["__EVENTTARGET"] in my SelectedIndexChanged event handlers. I'm still curious why it happens, though. Why is the first event repeated? And why does it only happen when the second postback happens from a control below the first one?

like image 967
Tom Hamming Avatar asked Feb 03 '12 22:02

Tom Hamming


3 Answers

Once you click on the back button the last visited page is shown as it is saved on the client is not sent back/requested again to/from the server, this including the last posted value like the once shown after you select the second DropDownlist right after hitting the back button.

The onload method on IE(for other browsers see:http://dean.edwards.name/weblog/2005/09/busted/) of the first page gets fired when you press the back button on the second so using this we can try to save on the client the postback action before redirecting the page, we save it on a hidden value with JS, then once the user goes back the onload methods evaluates if the hidden has a value and if so then you change the location of the page to itself so the page is requested again and all the posted values are cleared out:

page1.aspx

 <script type="text/javascript">
        function redir(){    
            var h=document.getElementById('hdR');
            if (h.value=='1'){
                h.value='0';
                document.location.href='page1.aspx';
            }         
        }

        function changeHids(){
            var h=document.getElementById('hdR');
            h.value='1';
        }
        </script>
    </head>
    <body onload="redir();">
        <form id="form1" runat="server">
        <div>
            <asp:DropDownList ID="DropDownList1" runat="server" AutoPostBack="true" onchange="changeHids();">
                <asp:ListItem>1</asp:ListItem>
                <asp:ListItem>2</asp:ListItem>
                <asp:ListItem>3</asp:ListItem>
                <asp:ListItem>4</asp:ListItem>
            </asp:DropDownList>
            <br />
            <asp:DropDownList ID="DropDownList2" runat="server" AutoPostBack="true" onchange="changeHids();">
                <asp:ListItem>5</asp:ListItem>
                <asp:ListItem>6</asp:ListItem>
                <asp:ListItem>7</asp:ListItem>
                <asp:ListItem>8</asp:ListItem>
            </asp:DropDownList>
            <asp:HiddenField ID="hdR" runat="server"/>
        </div>
        </form>
    </body>

Protected Sub DropDownList1_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles DropDownList1.SelectedIndexChanged

    Response.Redirect("page2.aspx?ddl=1&val=" & Me.DropDownList1.SelectedValue, True)

End Sub

Protected Sub DropDownList2_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles DropDownList2.SelectedIndexChanged

    Response.Redirect("page2.aspx?ddl=2&val=" & Me.DropDownList2.SelectedValue, True)

End Sub

page2.aspx

  Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load

        Response.Write("ddl:" & Request.QueryString("ddl") & " " & "value:" & Request.QueryString("val"))

    End Sub
like image 60
CoderRoller Avatar answered Nov 17 '22 19:11

CoderRoller


Firefox - Add Code Behind:

protected void Page_Load(object sender, EventArgs e)
{ 
    Response.AppendHeader("Cache-Control", "no-store");
}

Chrome - Add javascript pageload:

var is_chrome = navigator.userAgent.toLowerCase().indexOf('chrome') > -1;
if (is_chrome) {
    document.getElementById("form1").reset();
}
like image 3
João Zaiden Avatar answered Nov 17 '22 20:11

João Zaiden


you can use Response.Cache properties to solve this issue, as you are allowing to cache the list in browser memory. from MSDN SetAllowResponseInBrowserHistory

When HttpCacheability is set to NoCache or ServerAndNoCache the Expires HTTP header is by default set to -1; this tells the client not to cache responses in the History folder, so that when you use the back/forward buttons the client requests a new version of the response each time. You can override this behavior by calling the SetAllowResponseInBrowserHistory method with the allow parameter set to true.

in your page load method, you add this line .

 protected void Page_Load(object sender, EventArgs e)
  {
       Response.Cache.SetCacheability(HttpCacheability.NoCache); 
       Response.Cache.SetAllowResponseInBrowserHistory(false);

        // or else you can do like this 

       Response.ExpiresAbsolute = DateTime.Now.AddDays(-1d);
        Response.Expires = -1;
        Response.CacheControl = "No-cache";
  }

so that whenever you pressed back, t will requests a new version of the response each time.

like image 2
Ravi Gadag Avatar answered Nov 17 '22 20:11

Ravi Gadag