注销

注销

  BlogJava :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理 ::
  112 随笔 :: 7 文章 :: 18 评论 :: 0 Trackbacks

一、              DataGrid 控件中的 ViewState

   Asp.Net 中,我们使用最多的恐怕就是 DataGrid 列表控件了。这个控件的功能的却非常强大,也非常好用。它不但可以实现任意的格式化选择,而且还可以动态进行分页、排序、添加按钮、动态编辑等功能。可以说, DataGrid 控件已经实现了我们需要的大部分功能。

   遗憾的是,上面说的很多非常实用的功能,大部分需要 ViewState 的支持,就是说 DataGrid 控件需要通过 ViewState 来保存控件的状态的,如果我们关闭了 ViewState ,即在 HTML 代码中使用了 EnableViewState ="False" 属性,那么,上面的所有有用的功能我们将无法使用。

   ViewState 的缺点也是很大的,就是 DataGrid 会把所有的数据源中的数据存放到 ViewState 中。 DataGrid 是一个列表控件,它存放在 ViewState 中的数据包括了列表中所有单元格( Cell )中的数据,更加可恨的是,即时我们打开了分页功能,它也要把所有的没有显示出的数据源中的数据放到 ViewState 里面,如果不在数据库端控制数据量大小的话, DataGrid 控件实际上是把数据库表中所有数据统统都存放在 ViewState 中。直接后果就是 Asp.Net 生成发送到客户端的源 Html 代码异常庞大。

   大家知道, Asp.Net 中, ViewState 实际上是一个名称 / 值对的字典,为了正确在各种编码的网页中保存,将 ViewState 序列化后又进行了 Base64 编码。这将更加加大 ViewState 的数据量。

   我试验了一下,一个包括 5000 条记录的数据库,如果不在数据库端控制数据量的话,直接使用 Select * From TableName 这样的 Sql 语句绑定数据的话,一个 Asp.Net 动态网页(仅有一个 DataGrid 控件的测试页)的 ViewState 60k 之多。而这样一个网页放到公共 Web 网站,缓慢的网页下载速度将会使大部分人望而却步,使用 56k 猫上网的人就更不要提了,根本无法访问这样的网页,等待的时间将不可忍受。

   所以大部分有 Asp.Net 开发经验的开发者,如果不使用 DataGrid 的高级功能的话,一般是把 EnableViewState ="False" 属性给加入的,还有就是使用自己的分页控件,不使用 DataGrid 提供的分页功能,这将取消 DataGrid ViewState ,大大减少最终生成的 Html 的代码量。

 

二、 DataGrid 中必须使用 ViewState 的一些功能

   不错,很多情况下我们仅需要 DataGrid 的显示列表数据的功能,或者使用自己的分页功能,这种情况下可以使用 EnableViewState ="False" 属性,但是如果我们需要使用 DataGrid 的动态编辑、按钮列,或者要访问 DataGrid 的索引项时,我们必须把 DataGrid ViewState 打开,这将不可避免的出现 ViewState 数据量过大的现象,但是我们没有办法。

   我大略统计了一下 DataGrid 中需要使用 ViewState 的一些功能属性,它们都必须使用 ViewState ,而这些属性我们很多情况下都必须使用:

DataGrid.CurrentPageIndex                     DataGrid 分页的页索引

DataGrid.DataKeys                             取每个记录的键值

DataGrid.EditItemIndex                        编辑项的索引

DataGrid.Items                                DataGrid 中行的集合

DataGrid.PageCount                            DataGrid 分页的总页数

 

而在使用 DataGrid 内置事件时,传递事件参数的 DataGridItemEventArgs DataGridCommandEventArgs DataGridSortCommandEventArgs DataGridPageChangedEventArgs 类更加要使用 DataGrid ViewState ,否则它们将无法工作。

   这里我给一个简单的示例,在 DataGrid 中加入一个 CheckBox 列,这使当页面回发的时候我可以获取 DataGrid 的键值。但是如果关闭 ViewState 的话,此功能无法使用。

 

 

Html

< Asp:DataGrid ID=”myDataGrid”Runat=”Server”DataKeyField=”ID” AutoGenerateColumns=”False”>

    < Columns >

       < asp:TemplateColumn >

           < ItemTemplate >

              < Asp:CheckBox ID=”myBox”Runat=”Server”/>

           </ ItemTemplate >

       </ asp:TemplateColumn >

    </ Columns >

</ Asp:DataGrid >

< br />< br />

< Asp:Button ID= ”bntSubmit” Runat= ”Server” Text= ”OK” />

 

Code

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

{

    CheckBox cb;

    string str;

    for(int i=0;i<myDataGrid.tems.Count;i++)

    {

       cb = (CheckBox)myDataGrid.Items[i].Cells[2].FindControl("myBox");

       if(cb.Checked)

           str += myDataGrid.DataKeys[myDataGrid.Items[i].ItemIndex].ToString();

    }

    // 此时 str 变量就是 DataGrid 中选中行的数据库键值。

}

 

上面的代码使用很成功,不过如果在 DataGrid 中使用了 EnableViewState=”False” 属性,那么上面的代码根本无法起作用。


 

三、去掉 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; 的法子,如何选择,大家请自己斟酌。


posted on 2006-11-21 17:08 注销..... 阅读(438) 评论(0)  编辑  收藏 所属分类: .net摘要

只有注册用户登录后才能发表评论。


网站导航: