Search code examples
c#asp.netvalidationpostbackasynchronous-postback

Invalid postback or callback argument (HiddenField and container Visible=false)


I've not found answers that matched my circumstances, so I'm posting an answered question hoping it will help others.

I was getting the error

Invalid postback or callback argument. Event validation is enabled using in configuration or <%@ Page EnableEventValidation="true" %> in a page. For security purposes, this feature verifies that arguments to postback or callback events originate from the server control that originally rendered them. If the data is valid and expected, use the ClientScriptManager.RegisterForEventValidation method in order to register the postback or callback data for validation.

   at System.Web.UI.ClientScriptManager.ValidateEvent(String uniqueId, String argument)
   at System.Web.UI.Control.ValidateEvent(String uniqueID, String eventArgument)
   at System.Web.UI.WebControls.HiddenField.LoadPostData(String postDataKey, NameValueCollection postCollection)
   at System.Web.UI.WebControls.HiddenField.System.Web.UI.IPostBackDataHandler.LoadPostData(String postDataKey, NameValueCollection postCollection)
   at System.Web.UI.Page.ProcessPostData(NameValueCollection postData, Boolean fBeforeLoad)
   at System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint)

I've got a Databound ListView (with a few 100s rows), with buttons on each row. The buttons bring a popup. The popup has dropdownlists and other controls doing asynchronous postbacks. I need to make sure I do asynchronous postbacks to avoid refreshing my big table.

I get the error when I click the button on one row, then change a dropdownlist inside the popup which fires a postback (selected item changed). Boom.

Here's the markup for a reduced sample, without popup and javascript at all! It still exhibits the problem. Click twice on a button in a row to get the error.

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="TestPopupAsynchPostback.Default" %>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
</head>
<body>
    <form id="form1" runat="server">
        <asp:ScriptManager ID="scriptMgr" runat="server" ScriptMode="Debug" EnablePartialRendering="true"
            EnableScriptGlobalization="true" EnableScriptLocalization="true" EnablePageMethods="true"/>
        <asp:ObjectDataSource ID="ListDataSource" runat="server" SelectMethod="List" TypeName="TestPopupAsynchPostback.Default" />
        <asp:Label runat="server" ID="PageLabel"></asp:Label>
        <asp:ListView ID="EL" runat="server" DataSourceID="ListDataSource" OnItemDataBound="EntityList_OnItemDataBound">
            <LayoutTemplate>
                <table border="1">
                    <tr id="itemPlaceholder" runat="server" enableviewstate="false">
                    </tr>
                </table>
            </LayoutTemplate>
            <ItemTemplate>
                <tr runat="server" id="DefaultRow" enableviewstate="false">
                    <td>
                        <asp:Label ID="Lbl" runat="server" EnableViewState="false" />
                    </td>
                    <td>
                        <button runat="server" type="button" id="ReportingButton" enableviewstate="false" onserverclick="ReportingButton_OnClick" causesvalidation="false">click</button>
                    </td>
                </tr>
                <%-- Fix part 1: Change SpecialRow  visible = true--%>
                <tr runat="server" id="SpecialRow" visible="false" enableviewstate="false">
                    <td>
                        <asp:Label ID="Lbl2" runat="server" EnableViewState="false" />
                        <asp:HiddenField runat="server" ID="fn_hid" />
                    </td>
                </tr>
            </ItemTemplate>
        </asp:ListView>
    </form>
</body>
</html>

Here's the code behind:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;

namespace TestPopupAsynchPostback
{
    public partial class Default : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            ScriptManager sm = ScriptManager.GetCurrent(Page);
            PageLabel.Text = DateTime.UtcNow.ToString() + " IsPostBack:" + IsPostBack + " IsInAsyncPostBack:" + (sm == null ? "" : sm.IsInAsyncPostBack.ToString());
        }

        protected override void Render(HtmlTextWriter writer)
        {
            ScriptManager sm = ScriptManager.GetCurrent(this.Page);
            if (sm != null)
            {
                foreach (ListViewDataItem row in EL.Items)
                {
                    HtmlButton reportingButton = row.FindControl("ReportingButton") as HtmlButton;
                    if (reportingButton != null)
                        sm.RegisterAsyncPostBackControl(reportingButton);
                }
            }
            base.Render(writer);
        }

        public IList<string> List()
        {
            return (new string[] { "ONE", "TWO"}).ToList();
        }

        protected void ReportingButton_OnClick(object sender, EventArgs e)
        {
            //Do something useful here, for now, just a postback event
        }

        protected void EntityList_OnItemDataBound(object sender, ListViewItemEventArgs e)
        {
            Label lbl = e.Item.FindControl("Lbl") as Label;
            Label lbl2 = e.Item.FindControl("Lbl2") as Label;
            lbl.Text = lbl2.Text = e.Item.DataItem.ToString();

            HtmlTableRow specialRow = e.Item.FindControl("SpecialRow") as HtmlTableRow;
            if (e.Item.DataItemIndex%2 == 0)
            {
                HiddenField fn_hid = e.Item.FindControl("fn_hid") as HiddenField;
                fn_hid.Value = "test1";
                specialRow.Visible = true;
            }
            //Fix part 2: set SpecialRow Visible = false in code behind
            //else
            //    specialRow.Visible = false;
        }
    }
}

Solution

  • I initially thought my javascript was at fault as I am modifying things with it. However, the process of creating a sample page has helped me find the problem.

    It turns out it has nothing to do with the javascript or the popup. It's to do with a HiddenField contained inside a TR HtmlControl (asp.net server side control) with Visible=false IN THE MARKUP. I then use the code behind to set Visible=true when I need to in OnItemDataBound event.

    This is what is causing the error for me: It seems that because the container (the TR SpecialRow) is Visible=false, I guess something is not rendered. I then come along in OnItemDataBound, decide that this row must be shown, and set Visible=true and set the value of my HiddenField. Boom. If I remove the markup for the hidden field and don't set its value, no crash. So it's not just the visibility of the TR on its own, it's the HiddenField too.

    My fix: don't set Visible=false in the markup, and change OnItemDataBound to set Visible=false when required.

    In other words: I was defaulting to hide things in markup and use code behind to show them. I changed this around and show things by default in markup and hide them using the code behind.

    I've added the fix in the markup and code above.