开发者

Double Postback from Firefox in asp.net application

Firefox is causing double PostBacks in my asp.net application. IE and Safari do not cause the problem.

I have a form for users to fill out that is inside the second View shown in a MultiView. The MultiView is inside an UpdatePanel so the Views are updated via partial page postbacks. Once they are presented the form they hit submit and their entries are stored in the DB.

In order to prevent users from doubleclicking the submit button I'm using Javascript to disable the button once the form has been submitted. There are several techniques on the web that show how to do this, and I've used this particular solution for quite some time. As I mentioned, only Firefox causes a double PostBack. This seems to be a common problem and if you Google for it you'll find a number of discussions, but I haven’t really found an answer yet. Most of the discussions revolve around the src attribute of an being empty. I am using an ImageButton, but the src attribute should never be empty in my case.

Lets look at the code.

First the markup (abbreviated):

<asp:UpdatePanel ID="UP1" runat="server">
  <ContentTemplate>
    <asp:MultiView id="MV1" runat="server" ActiveViewIndex="0">
      <asp:View id="View1" runat="server">
        Some markup and some asp.net controls
      </asp:View>
      <asp:View id="View2" runat="server">
        <asp:TextBox ID="NameTB" runat="server" />
        <asp:RequiredFieldValidator ID="NameTBRFV" runat="server"
             ControlToValidate="NameTB" ErrorMessage="Required" 
             CssClass="validate" />
        <asp:ImageButton ID="Submit" runat="server"
             ImageUrl="Images/AlumniSubmitButton.jpg" OnClick="Submit_Click" />
      </asp:View>
    </asp:MultiView>
  <ContentTemplate>
</asp:UpdatePanel>

Second the code behind Page Load:

protected void Page_Load(object sender, EventArgs e)
    {
        if (!IsPostBack)
        {
            Utilities.BindSafeClick(Submit, "Images/AlumniSubmitButton.jpg");
        }
    }

Third the code that adds the onclick which disables the button:

public static void BindSafeClick(Control control, String waitMessage)
{
    StringBuilder sbValid = new StringBuilder();

    Page myPage = control.Page;

    if (control is Button)
    {

        Button myControl = (Button)control;

        sbValid.Append("if (typeof(Page_ClientValidate) == 'function') { ");

        sbValid.Append("if (Page_ClientValidate() == false) { return false; }} ");

        sbValid.Append("this.value = '" + waitMessage + "';");

        sbValid.Append("this.disabled = true;");

        sbValid.Append(myPage.ClientScript.GetPostBackEventReference(myControl, null));

        sbValid.Append(";");

        // the following depends on if submit control has UseSubmitBehavior set.
        // Basically set the opposite here.

        if (myControl.UseSubmitBehavior)
        {
            sbValid.Append("return false;");
        }
        else
        {
            sbValid.Append("return true;");
        }

        myControl.Attributes.Add("onclick", sbValid.ToString());
    }
    else if (control is LinkButton)
    {
        LinkButton myControl = (LinkButton)control;

        sbValid.Append("if (typeof(Page_ClientValidate) == 'function') { ");

        sbValid.Append("if (Page_ClientValidate() == false) { return false; }} ");

        sbValid.Append("this.innerText = '" + waitMessage + "';");

        sbValid.Append("this.disabled = \"disabled\";");

        sbValid.Append(myPage.ClientScript.GetPostBackEventReference(myControl, null));

        sbValid.Append(";");

        myControl.Attributes.Add("onclick", sbValid.ToString());
    }
    else if (control is ImageButton)
    {
        ImageButton myControl = (ImageButton)control;

        sbValid.Append("if (typeof(Page_ClientValidate) == 'function') { ");

        sbValid.Append("if (Page_ClientValidate() == false) { return false; }} ");

        sbValid.Append("this.src = '" + waitMessage + "';");

        sbValid.Append("this.disabled = true;");

        sbValid.Append(myPage.ClientScript.GetPostBackEventReference(myControl, null));

        sbValid.Append(";");

        myControl.Attributes.Add("onclick", sbValid.ToString());
    }
}

I looked at the rendered output when the browser receives the form and noticed that the onclick javascript seems to do two Postbacks.

The same code is rendered in Safari and IE.

Why would Firefox be the only one that does two PostBacks?

Here's the rendered output (added line breaks and spacing to onclick for readability)

<input type="image" name="Submit" id="Submit" src="Images/AlumniSubmitButton.jpg"
 onclick="
          if (typeof(Page_ClientValidate) == 'function') 
           { 
            if (Page_ClientValidate() == false)
             {
              return false; 
             }
           } 
          this.src = 'Images/AlumniSubmitButton.jpg';
          this.disabled = true;
          __doPostBack('Submit','');
          WebForm_DoPostBackWithOptions(new WebForm_Pos开发者_StackOverflowtBackOptions(&quot;Submit&quot;,     &quot;&quot;, true, &quot;&quot;, &quot;&quot;, false, false))" style="border-width:0px;" />


It is simplily because the image path is wrong. When FireFox couldn't find the image, it will do postback again. It took me a while to figure it out.


Firefox requires return false; at the end of the JavaScript statement. Adding return false; at the end of the JavaScript statement will be interpreted correctly by the other browsers as well.

So instead of writing this;

yourButtonControl.Attributes.Add("onclick", "if (Page_ClientValidate('nameOfValidationGroup') == false) { return false; } " + ``Page.ClientScript.GetPostBackEventReference(yourButtonControl, String.Empty) + ";this.disabled = true;");

...write this;

yourButtonControl.Attributes.Add("onclick", "if (Page_ClientValidate('nameOfValidationGroup') == false) { return false; } " + Page.ClientScript.GetPostBackEventReference(yourButtonControl, String.Empty) + ";this.disabled = true; return false;");

It took me a few days of trial and error, as well as a lot of testing to determine why my data layer logic just all of a sudden began throwing the following error:

Transaction (Process ID "...") was deadlocked on lock resources with another process and has been chosen as the deadlock victim. Rerun the transaction.

...only to find out that Firefox was the culprit.

At the end of the day, it's bugs like these that improve your knowledge and skill. ;-]

I hope this helps a lot of other developers.

JavaScript isn't evil. It's just not as friendly as managed code.

Patrick


Try adding a postback trigger to the button. Trigger section above the closing UpdatePanel tag.

Sample Code:

<asp:UpdatePanel ID="UP1" runat="server">      
  <ContentTemplate>      
    <asp:MultiView id="MV1" runat="server" ActiveViewIndex="0">      
      <asp:View id="View1" runat="server">      
        Some markup and some asp.net controls      
      </asp:View>      
      <asp:View id="View2" runat="server">      
        <asp:TextBox ID="NameTB" runat="server" />      
        <asp:RequiredFieldValidator ID="NameTBRFV" runat="server"      
             ControlToValidate="NameTB" ErrorMessage="Required"       
             CssClass="validate" />      
        <asp:ImageButton ID="Submit" runat="server"      
             ImageUrl="Images/AlumniSubmitButton.jpg" OnClick="Submit_Click" />      
      </asp:View>      
    </asp:MultiView>      
  </ContentTemplate>
  <Triggers>
    <asp:PostBackTrigger ControlID="Submit" />
  </Triggers>      
</asp:UpdatePanel>


Time ago I was having the same problem with an ImageButton, and the problem disappeared when I set the ImageButton AlternateText attribute to a not empty string.


One workaround is to put the button in a DataGridView. This will prevent double postbacks with FireFox.

Create the DataGridView:

       <asp:GridView ID="GridViewButton" ShowHeader="false" runat="server" AutoGenerateColumns="false" 
                  GridLines="None" OnRowCommand="GridViewButton_RowCommand" CellPadding="0" OnDataBound="GridViewButton_DataBound">
            <Columns>
                <asp:ButtonField Text="Save" CommandName="Save" ButtonType="Button">
                </asp:ButtonField>
            </Columns>
       </asp:GridView>

On PageLoad, populate the DataGridView with dummy data so that the button gets displayed:

Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs)
    Dim dt As New DataTable
    dt.Columns.Add(New DataColumn("col1"))
    Dim dr As DataRow = dt.NewRow
    dt.Rows.Add(dr)
    Me.GridViewButton.DataSource = dt
    Me.GridViewButton.DataBind()
End Sub

On DataBound, set the javascript to disable the button onclick:

Protected Sub GridViewButton_DataBound(ByVal sender As Object, ByVal e As System.EventArgs)

    CType(Me.GridViewButton.Rows(0).Cells(0).Controls.Item(0), Button).Attributes.Add("onclick", "this.disabled=true;")

End Sub

Handle the click event:

Protected Sub GridViewButton_RowCommand(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.GridViewCommandEventArgs)

    If e.CommandName = "Save" Then

         //Do something here

    End If

End Sub


It seems that _doPostBack() and WebForm_DoPostBackWithOptions() both are doing the post back. Both are conflicting each other. For more refer this

Quite interesting..

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜