去除Asp:DataGrid中无用ViewState的方法(2)

** 三、去掉 ** ** ViewState ** ** 中 ** ** DataGrid ** ** 的 无用 数据 **

终于说到正题了,我这篇文章说的就是怎样去掉 ViewState 中的无用数据,而保留有用的数据。文章写到这里,我们可以很明显的看出来, DataGrid 保存在 ViewState 中的数据分为两个部分,一部分是保存索引用的,就是 DataKeys 和 DataItems 这样的属性使用的数据,我们把它称之为索引数据。还有一部分是 DataGrid 中数据源的内容,我们称之为列表数据。

我们如果把实际上无用的列表数据从 ViewState 中去除,这样可以大大减小页面 ViewState 的数据大小,使用 DataGrid 时 ViewState 数据量太大的根本原因就是列表数据存放在 ViewState 中。

我在微软的 .Net Framework SDK 文档中没有找到关闭列表数据 ViewState 的任何内容,就是说微软没有给出 DataGrid 运行时的任何调用顺序和内部的工作机制。没办法,我使用了一个工具来获得 DataGrid 的内部工作流程,发现它在数据绑定初始化的时候,生成了一个叫 ** DataGridTable ** 的控件对象,这个对象是继承 System.Web.UI.WebControls.Table 控件的。而且这个对象是最先加入(使用 Controls.Add() 方法) DataGrid 中的。而且 ViewState 中的 DataGrid 列表数据也是这个控件加入到 DataGrid 中的。实际上, ViewState 中的 DataGrid 的单元格中的数据实际上是 System.Web.UI.WebControls.Table 控件的 SaveViewState() 方法给加进去的。这些数据很多情况下是不需要的。

好,原因找到了,解决问题就好办了,因为微软已经给出了控制 DataGrid 中子控件的方法。我们既然知道 DataGridTable 控件是 DataGrid 中最先生成的控件,那么我们通过 DataGrid.Controls 属性就可以直接获取 DataGrid 中子控件的引用,获得引用后就可以控制子控件了。解决方法就是在数据绑定的时候,设置 DataGrid 中 DataGridTable 控件的 EnableViewState 属性为 False 就可以了。

代码如下:

首先必须重写 DataGrid.ItemDataBound 或 DataGrid.ItemCreated 事件,我们用它们来控制在向客户端写 html 之前 DataGrid 的动作。这两个事件任选其一,都可以实现效果。我们使用 ItemDataBound 事件来写例子, DataGrid 示例 ID 为 myDataGrid 。

[C#]

首先在页面初始化中的 InitializeComponent() 方法内加入事件的委托:

private void InitializeComponent()

{

this.myDataGrid.ItemDataBound +=

new DataGridItemEventHandler( this .myDataGrid_ItemDataBound);

}

然后在 myDataGrid_ItemDataBound 方法内加入控制代码:

private void myDataGrid_ItemDataBound( object sender, DataGridItemEventArgs e)

{

myDataGrid.Controls[0].EnableViewState = false ;

}

[VB.Net]

Private Sub myDataGrid_ItemDataBound(sender As Object ,e As DataGridItemEventArgs) _

Handles myDataGrid.ItemDataBound

myDataGrid.Contols.Item(0).EnableViewState = False

End Sub

好了,使用 DataGrid 时,把上面的代码加入,将减小使用 DataGrid 时 ViewState 的 90% 的数据量。而且, DataGrid 中许多使用 ViewState 的功能丝毫不少,岂不是两全其美?

** 注意: ** ** **

1. 上面说的法子有一种情况下不能使用,就是使用 DataGrid 的内部分页功能时,重写 DataGrid. PageIndexChanged 事件时,调用 DataGridPageChangedEventArgs 时,必须把所有的 ViewState 打开,包括列表数据,关闭任何的 ViewState ,都将导致 DataGridPageChangedEventArgs 的索引丢失,无法分页。

不过这个缺点很好解决,很多人已经写了自定义的分页控件,这些控件是不需要 DataGrid 的任何数据的。可以提供非常完美的分页功能。

2. (重要)我春节的这几天对上面说的办法进行了详细的测试,发现上面的办法确实有效,可以大大的减少 aspx 页面的 ViewState 的数据量,但是这个办法在使用上有一个注意的地方,否则会出现数据无法显示的毛病。

我一直在奇怪,微软写 DataGrid 为什么要加入一个 DataGridTable 类,而且还要把所有的数据库数据存放在 ViewState 中。经过测试我才发现,这是因为有了 System.Web.UI.Page. IsPostBack 属性的缘故。我们经常喜欢利用 Page. IsPostBack 属性检测页面是否是第一次运行,如下代码:

private void Page_Load( object sender, System.EventArgs e)

{

if (!Page.IsPostBack)

{

// 页面第一次运行 , 执行数据绑定

SqlConnection conn = new SqlConnection(“...”);

SqlCommand comm = new SqlCommand(“...”,conn);

...

...

myDataGrid.DataBind() // 数据绑定

}

}

当然,网页执行第一次时, DataGrid 的内容正常显示,而使用了上面的去除 ViewState 方法后,页面如果回发处理, DataGrid 的内容将会消失。我才明白 DataGrid 内 DataGridTable 把数据存放在 ViewState 内的用意。微软的设计是非常严谨的,他们的用意就是当使用 Page. IsPostBack 属性时,仅访问一次数据库就可以永久保持 DataGrid 的数据(在不离开此页面的情况下),数据存放的地点就是页面的 ViewState 中。这样页面回发后, DataGrid 就可以从 ViewState 中重新生成 DataGrid 的显示内容,无需访问数据库。所以说微软以牺牲客户下载的速度( ViewState 数据量)来保证服务器的资源,大家都知道频繁访问数据库对服务器的资源消耗很大。

所以,使用上面减少 DataGrid 的 ViewState 数据的法子是可行的,但是必须使所有的页面回发处理都必须进行数据绑定,否则 DataGrid 无法获得数据库内容,也无法获得 ViewState 中保存的数据,那么回发后 DataGrid 将无法显示任何内容。

总结,使用上面减少 ViewState 的办法可以大大加快客户端的下载显示速度,但是频繁的数据库访问将加大服务器的压力;使用 ViewState 可以减轻服务器的压力,但是又加大了客户端的下载时间,它们是互相矛盾的。所以开发者要根据实际情况选择是否使用 DataGrid.Controls[0].EnableViewState=false; 的法子,如何选择,大家请自己斟酌。

Published At
Categories with Web编程
Tagged with
comments powered by Disqus