asp.net GridView 曾经能够使用 ViewState 吗?

Was the asp.net GridView ever capable of using ViewState?

我正在处理一个起源于 Visual Studio 网站项目的旧站点,除了其他工作之外,我正在将其转换为 Web 应用程序。站点上的一个页面使用 GridView,并使用 DataTable 在代码隐藏中设置 DataSource。 GridView 公开了一对 BoundField,以及一个带有复选框的 TemplateField。 GridView 配置为使用 EnableViewState。

<asp:GridView ID="Results" runat="server" AutoGenerateColumns="False" CellPadding="4" ForeColor="#333333" GridLines="None" EnableViewState="true">
    <AlternatingRowStyle BackColor="White" ForeColor="#284775" />
    <Columns>
        <asp:BoundField DataField="Type" HeaderText="Type"/>
        <asp:BoundField DataField="ProcessDate" HeaderText="Process Date" />
        <asp:BoundField DataField="Classification" HeaderText="Classification" />
        <asp:BoundField DataField="Email" HeaderText="Email" />
        <asp:TemplateField HeaderText="Notify?" ItemStyle-HorizontalAlign="Center">
            <EditItemTemplate>
                <asp:CheckBox ID="CheckBox1" runat="server" onclick="EnableSubmit(this);" />
            </EditItemTemplate>
            <ItemTemplate>
                <asp:CheckBox ID="CheckBox1" runat="server" onclick="EnableSubmit(this);" />
            </ItemTemplate>
        </asp:TemplateField>
    </Columns>
</asp:GridView>

该页面还有一个提交按钮,在 _Click 处理程序中,代码尝试抓取 Results.Rows,然后四处挖掘以确定哪些行的复选框已被选中。它根据选中的行进行后续处理。

自从我一直致力于此,我发现只有线程和文章讨论 GridView 如何实际上不使用 ViewState,以及如何使用发送给用户的数据,将 DataTable 用作数据源必须在后面的代码中手动插入ViewState等

我正在使用的站点版本已转换为 Web 应用程序项目,并且它的目标是 .NET 4.5。当我调试站点并进入 Page_LoadSubmit_Click 时,Results GridView 的数据源为空,行 属性 的计数为 0。这似乎与当前关于 GridView "works."

我很清楚,对于这个网站正在做的事情,这是一个令人发指的实施,而且有更好的方法来做到这一点。然而,我最担心的是我找不到任何关于旧版本如何工作的解释。

GridView 是否在其历史的某个时刻更改为忽略其 EnableViewState 属性? GridView 上的 EnableViewState 实际上做了什么?这可能是网站和 Web 应用程序项目之间的区别吗?

这怎么可能有效?

更新:基于 Bert Evans 的示例页面,我试用了这个修改后的页面。

<%@ Page Language="C#" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<script runat="server">
    public class Thing
    {
        public int ID { get; set; }
        public string Name { get; set; }
    }
    IEnumerable<Thing> Things
    {
        get
        {
            var things = new List<Thing>();
            things.Add(new Thing { ID = 1, Name = "One" });
            things.Add(new Thing { ID = 2, Name = "Two" });
            things.Add(new Thing { ID = 3, Name = "Three" });
            things.Add(new Thing { ID = 4, Name = "Four" });
            things.Add(new Thing { ID = 5, Name = "Five" });
            return things;
        }
    }

    void Page_Load(object sender, EventArgs e)
    {
        if (!IsPostBack)
        {
            ThingGridView.DataSource = Things;
            ThingGridView.DataBind();
        }

        pageLoadLabel.Text = string.Format("On page Load, row count: '{0}'", ThingGridView.Rows.Count);
    }

    void OnClickMe(object sender, EventArgs e)
    {
        onClickLabel.Text = string.Format("On click, row count: '{0}'", ThingGridView.Rows.Count);
    }

</script>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <asp:GridView runat="server" ID="ThingGridView">
            <Columns>
                <asp:BoundField HeaderText="Name" DataField="Name" />
            </Columns>
        </asp:GridView>
    </div>
        <asp:Button runat="server" ID="ThingButton" Text="Click Me!" OnClick="OnClickMe" />
        <asp:Label runat="server" ID="onClickLabel" Text="[set on click]" />
        <asp:Label runat="server" ID="pageLoadLabel" Text="[set on page load]" />
    </form>
</body>
</html>

唯一的区别是页面正文中打印行数的标签。执行此页面时。第一个 Page_Load 调用将行数 5 打印到页面;但是,在回发中,页面加载和 OnClick 方法中的行数均为 0。

数据绑定是将控件的指定数据源转换为呈现给页面的控件树的过程。对于 GridViews,本质上,设置 DataSource 属性 并调用 DataBind 方法转换 IEnumerable 或从另一个来源提取的 IEnumerableDataSetDataTable,变成带有包含数据的控件的 HTML table。

渲染到 table 单元格中的每个控件都维护它自己的 ViewState,因此当页面 post 返回服务器时, GridView重建控制结构,并用 ViewState 中的数据填充控件(或者更确切地说,页面启动控制结构的重建,GridView 只是参与)。

DataSource 未保存到 ViewState,仅保存呈现控件的状态。

禁用 GridView 上的 ViewState 会阻止它保存其自身状态的某些元素,这将阻止它执行分页和排序等操作。此外,如果您在 GridView 上禁用 ViewState,然后执行 postback(在客户端触发一个事件,将页面提交回服务器),那么 GridView 将在重新呈现页面时不显示任何内容,因为 EnableViewState 是继承的,子控件将无法保存自己的状态。在 post 返回后让 GridView 和禁用 ViewState 再次显示数据的唯一方法是重新绑定数据(使用数据再次调用 DataBind有数据的源)或在 GridView 中包含的子控件上手动启用 ViewState 并禁用 ViewState。您提到您正在处理的页面上的 DataBind!IsPostback 保护,因此它仅在页面的初始加载时绑定。

我不知道有什么变化使得 GridView 在某个时间点拯救了 DataSource,我相信这就是它一直有效的方式。

感谢 Bert Evans 和他的示例页面,我最终确定了问题所在。该错误实际上存在于 UnityHttpModule 的非常糟糕的 MSDN 代码示例中,它将 Unity DI 与 ASP.NET 集成。 UnityHttpModule 详见 https://msdn.microsoft.com/en-us/library/ff664534(v=pandp.50).aspx

除了实际上什至没有像列出的那样进行编译之外,class 在 InitComplete 事件上连接 DI 代码,该事件发生在加载 ViewState 之前,如本文所述: .就我而言,移动 DI 代码以在 PreLoad 上执行解决了我的问题。

最后,为了完整起见,以及对于其他在使用 MSDN Unity DI HttpModule 时遇到问题的人,另一个 SO 线程还提供了一个工作示例:.