一、
????????????
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;
的法子,如何選擇,大家請自己斟酌。