主题 + 动态控件创建 + Invisible UserControl = 视图状态错误?

Theme + dynamic control creation + Invisible UserControl = view state error?

default2.aspx

<%@ Page Language="C#" AutoEventWireup="true" Theme="Blue" %>

<%@ Register TagPrefix="uc1" TagName="favicon" Src="~/FavIcon.ascx" %>

<!DOCTYPE html>

<script runat="server">


    private void Page_PreRender(object sender, System.EventArgs e)
    {
        HtmlGenericControl scriptControl = new HtmlGenericControl("script");
        Page.Header.Controls.AddAt(0, scriptControl);//If this line is commented out, no exception will occur.
    }


    private void Page_Init(object sender, System.EventArgs e)
    {
        ScriptManager oSM = new ScriptManager();
        Page.Form.Controls.Add(oSM);//If this line is commented out, no exception will occur.
    }


</script>

<html>
<head runat="server">
    <title></title>
    <uc1:favicon runat="server"></uc1:favicon>
</head>
<body>
    <form id="form1" runat="server">
        <asp:Button ID="Button1" runat="server" Text="Button" />
    </form>
</body>
</html>

FavIcon.ascx

<%@ Control Language="C#" ClassName="FavIcon" AutoEventWireup="true" %>

<script runat="server">

    void Page_Load(object sender, EventArgs e)
    {
        this.Visible = false;//If this line is commented out, no exception will occur.
    }

</script>

同时向蓝色主题添加样式表。

页面打开成功,但点击按钮后抛出异常

Failed to load viewstate. The control tree into which viewstate is being loaded must match the control tree that was used to save viewstate during the previous request. For example, when adding controls dynamically, the controls added during a post-back must match the type and position of the controls added during the initial request.

谁能解释为什么会出现这个错误?

这是由于 ViewState 的性质造成的。简而言之,当您的页面被回发时,ViewState 恢复到的控件与控件索引匹配,因此当索引更改时会导致此问题。 在这种情况下,以这种方式为您的页面设置 属性 ViewStateMode="Disabled" 或更改您添加动态元素的顺序 Page.Header.Controls.AddAt(Page.Header.Controls.Count, scriptControl),这样您就不会干扰恢复元素的顺序,这可能会有所帮助你应付的麻烦。至少,它对我有用。

为了调试这些错误,我强烈建议启用跟踪(@page 指令中的 Trace="true",或使用 global setting in web.config)以便您可以看到服务器的控制树。

在 GET 上,控件树是这样的(注意自动生成的唯一 ID):

  HtmlHead
    + ctl05 : HtmlGenericControl (from your PreRender code)
    + ctl01 : Title (the <title> tag)
    + ctl02 : FavIcon (your favicon.ascx)
    + ctl04 : HtmlLink (from the 'Blue' theme)

  HtmlForm
    + ctl03 : ScriptManager

'Failed To Load ViewState' 错误的根本原因是:

  • 您使用了 ViewState。这是一个在许多情况下可以禁用的选项。
  • 您不使用固定 ID(这就是为什么 Visual Studio 的 IDE 始终向控件添加自动但固定的 ID)。
  • 您在树 中后期动态添加控件,因为它更改了 head 的控件树。另外,您在其他人之前添加它 (AddAt)。

生成的 ID 遵循 ASP.NET 的 lifecycle order:

  1. 标题(静态)
  2. 收藏夹图标(静态)
  3. 脚本管理器(初始化)
  4. 主题(初始化,覆盖后)
  5. 动态控件(在本例中为 Init 之后)

ASP.NET ViewState 引擎是一棵树 serializer/deserializer。树中的每个节点都有自己的 ID,加上由 parent 的 ID 和自己的 ID 组成的 "full ID"。当然,一旦您在序列化和反序列化之间更改 ID,所有的赌注都会关闭,引擎将检测到它并引发 'Failed to Load ViewState' 错误。

因此,如果您输入 Visible=false,您将使用 ViewState。如果你删除它,你不会。当您自己不使用 ViewState 时,出现 'Failed to load viewstate' 错误的可能性较小,但是 ASP.NET 可以在您使用其所有功能(主题等)时代表您使用某些 ViewState。在 Visible 的情况下,它只是意味着控件在那里(并使用 ViewState),但未呈现(它的 Render Size 为 0)。但是您会看到使用 ViewState 的任何其他 属性 的问题,它不是特定于 Visible 属性(您也可以尝试 this.ViewState["test"] = "whatever")。

如果您将 <uc1:favicon runat="server"></uc1:favicon> 放在页面的其他位置,它也会起作用,因为它不再位于 Head 控件树中,并且不会干扰主题的 link 或您的动态控制。

对于Theme和ScriptManager,只是你玩的时候,ID变了,也变了,也变了,系统也可能检测不到。

打破 ViewState 的方法有无数种。真正困难的是它似乎在不应该工作的时候工作(例如,只要您不使用 FavIcon 的 ViewState,您就不会注意到您的代码存在问题)。