閱讀971 返回首頁    go 阿裏雲 go 技術社區[雲棲]


ASP.NET ViewState 初探

與剛接觸ASP.NET頁麵的開發人員交談時,他們通常向我提出的第一個問題就是:“那個ViewState到底是什麼?”他們的語氣中流露出的那種感覺,就象我來到一家異國情調的餐館,侍者端上一道我從未見過的菜肴時的那種感覺-既疑惑不解,又充滿好奇。但肯定有人認為它不錯,否則就不會提供了。所以,我會先嚐一嚐,或許會喜歡上它,盡管它看上去的確很古怪!

對於ViewState也是如此,但是如果適應了它的風格,您會發現在許多情況下,您將樂於在自己的ASP.NET應用程序中使用ViewState,因為它可以幫助您使用更少的代碼完成更多的工作。但是,有時也會對ViewState完全棄之不用。下麵我們就這兩種情況分別進行闡述,不過,讓我們先回答什麼是ViewState這個問題。

答案:ViewState用於維護頁麵的UI狀態

Web是沒有狀態的,ASP.NET頁麵也沒有狀態,它們在到服務器的每個往返過程中被實例化、執行、呈現和處理。作為Web開發人員,您可以使用眾所周知的技術(如以會話狀態將狀態存儲在服務器上,或將頁麵回傳到自身)來添加狀態。下麵我們以圖1中的注冊窗體為例進行論述。

 
圖1:恢複回傳的窗體值

從上圖中可以看出,我為便餐選擇了一個無效的值。此窗體與Web上的多數窗體一樣友好,它在出現錯誤的字段旁邊顯示一條有用的錯誤消息和一個星號。而且,窗體中還顯示了我在其他文本框和下拉列表中輸入的所有有效值。這在某種程度上是可能的,因為HTML窗體元素會在HTTP標頭中將其當前值從瀏覽器發送到服務器。您可以使用ASP.NET跟蹤來查看回傳的窗體值,如圖2所示。

 
圖2:HTTP窗體中回傳的值(通過ASP.NET跟蹤顯示)

在ASP.NET之前,通過多次回傳將值恢複到窗體字段中完全是頁麵開發人員的責任,他們將不得不從HTTP窗體中逐個拾取回傳值,然後再將其推回字段中。幸運的是,現在ASP.NET可以自動完成這項任務,從而為開發人員免除了一項令人厭煩的工作,同時也無需再為窗體編寫大量的代碼。但這並不是ViewState。

ViewState(英文)是一種機製,ASP.NET使用這種機製來跟蹤服務器控件狀態值,否則這些值將不作為HTTP窗體的一部分而回傳。例如,由Label控件顯示的文本默認情況下就保存在ViewState中。作為開發人員,您可以綁定數據,或在首次加載該頁麵時僅對Label編程設置一次,在後續的回傳中,該標簽文本將自動從ViewState中重新填充。因此,除了可以減少繁瑣的工作和代碼外,ViewState通常還可以減少數據庫的往返次數。

ViewState的工作原理

ViewState確實沒有什麼神秘之處,它是由ASP.NET頁麵框架管理的一個隱藏的窗體字段。當ASP.NET執行某個頁麵時,該頁麵上的ViewState值和所有控件將被收集並格式化成一個編碼字符串,然後被分配給隱藏窗體字段的值屬性(即<inputtype=hidden>)。由於隱藏窗體字段是發送到客戶端的頁麵的一部分,所以ViewState值被臨時存儲在客戶端的瀏覽器中。如果客戶端選擇將該頁麵回傳給服務器,則ViewState字符串也將被回傳。在上麵的圖2中可以看到ViewState窗體字段及其回傳的值。

回傳後,ASP.NET頁麵框架將解析ViewState字符串,並為該頁麵和各個控件填充ViewState屬性。然後,控件再使用ViewState數據將自己重新恢複為以前的狀態。

關於ViewState還有三個值得注意的小問題。

如果要使用ViewState,則在ASPX頁麵中必須有一個服務器端窗體標記(<formrunat=server>)。窗體字段是必需的,這樣包含ViewState信息的隱藏字段才能回傳給服務器。而且,該窗體還必須是服務器端的窗體,這樣在服務器上執行該頁麵時,ASP.NET頁麵框架才能添加隱藏的字段。

頁麵本身將20字節左右的信息保存在ViewState中,用於在回傳時將PostBack數據和ViewState值分發給正確的控件。因此,即使該頁麵或應用程序禁用了ViewState,仍可以在ViewState中看到少量的剩餘字節。

在頁麵不回傳的情況下,可以通過省略服務器端的<form>標記來去除頁麵中的ViewState。

充分利用ViewState

ViewState為跨回傳跟蹤控件的狀態提供了一條神奇的途徑,因為它不使用服務器資源、不會超時,並且適用於任何瀏覽器。如果您要編寫控件,那麼肯定需要了解如何在控件中維護狀態(英文)。

開發人員在編寫頁麵時同樣可以按照幾乎相同的方式來利用ViewState,隻是有時頁麵會包含不由控件存儲的UI狀態值。您可以跟蹤ViewState中的值,使用的編程語法與會話和高速緩存的語法類似:

[VisualBasic]


'保存在ViewState中
ViewState("SortOrder")="DESC"

'從ViewState中讀取
DimSortOrderAsString=CStr(ViewState("SortOrder"))

[C#]


//保存在ViewState中
ViewState["SortOrder"]="DESC";

//從ViewState中讀取
stringsortOrder=(string)ViewState["SortOrder"];

請看下麵的示例:要在Web頁上顯示一個項目列表,而每個用戶需要不同的列表排序。項目列表是靜態的,因此可以將這些頁麵綁定到相同的緩存數據集,而排序順序隻是用戶特定的UI狀態的一小部分。ViewState非常適合於存儲這種類型的值。代碼如下:

[VisualBasic]


<%@ImportNamespace="System.Data"%>
<HTML>
<HEAD>
<title>用於頁麵UI狀態值的ViewState/title>
</HEAD>
<body>
<formrunat="server">
<H3>
在ViewState中存儲非控件狀態
</H3>
<P>
此示例將一列靜態數據的當前排序順序存儲在ViewState中。<br>
單擊列標題中的鏈接,可按該字段排序數據。<br>
再次單擊該鏈接,將按相反順序排序。
<br><br><br>
<asp:datagridrunat="server"
OnSortCommand="SortGrid"BorderStyle="None"BorderWidth="1px"
BorderColor="#CCCCCC"BackColor="White"CellPadding="5"AllowSorting="True">
<HeaderStyleFont-Bold="True"ForeColor="White"
BackColor="#006699">
</HeaderStyle>
</asp:datagrid>
</P>
</form>
</body>
</HTML>
<scriptrunat="server">

'在ViewState中跟蹤SortField屬性
PropertySortField()AsString

Get
DimoAsObject=ViewState("SortField")
IfoIsNothingThen
ReturnString.Empty
EndIf
ReturnCStr(o)
EndGet

Set(ValueAsString)
IfValue=SortFieldThen
'與當前排序文件相同,切換排序方向
SortAscending=NotSortAscending
EndIf
ViewState("SortField")=Value
EndSet

EndProperty

'在ViewState中跟蹤SortAscending屬性
PropertySortAscending()AsBoolean

Get
DimoAsObject=ViewState("SortAscending")
IfoIsNothingThen
ReturnTrue
EndIf
ReturnCBool(o)
EndGet

Set(ValueAsBoolean)
ViewState("SortAscending")=Value
EndSet

EndProperty

PrivateSubPage_Load(senderAsObject,eAsEventArgs)HandlesMyBase.Load

IfNotPage.IsPostBackThen
BindGrid()
EndIf

EndSub

SubBindGrid()

'獲取數據
DimdsAsNewDataSet()
ds.ReadXml(Server.MapPath("TestData.xml"))

DimdvAsNewDataView(ds.Tables(0))

'應用排序過濾器和方向
dv.Sort=SortField
IfNotSortAscendingThen
dv.Sort+="DESC"
EndIf

'綁定網格
DataGrid1.DataSource=dv
DataGrid1.DataBind()

EndSub

PrivateSubSortGrid(senderAsObject,eAsDataGridSortCommandEventArgs)
DataGrid1.CurrentPageIndex=0
SortField=e.SortExpression
BindGrid()
EndSub

</script>

[C#]


<%@PageLanguage="C#"%>
<%@ImportNamespace="System.Data"%>
<HTML>
<HEAD>
<title>用於頁麵UI狀態值的ViewState</title>
</HEAD>
<body>
<formrunat="server">
<H3>
在ViewState中存儲非控件狀態
</H3>
<P>
此示例將一列靜態數據的當前排序順序存儲在ViewState中。<br>
單擊列標題中的鏈接,可按該字段排序數據。<br>
再次單擊該鏈接,將按相反順序排序。
<br><br><br>
<asp:datagridrunat="server"OnSortCommand="SortGrid"
BorderStyle="None"BorderWidth="1px"BorderColor="#CCCCCC"
BackColor="White"CellPadding="5"AllowSorting="True">
<HeaderStyleFont-Bold="True"ForeColor="White"BackColor="#006699">
</HeaderStyle>
</asp:datagrid>
</P>
</form>
</body>
</HTML>
<scriptrunat="server">

//在ViewState中跟蹤SortField屬性
stringSortField{

get{
objecto=ViewState["SortField"];
if(o==null){
returnString.Empty;
}
return(string)o;
}

set{
if(value==SortField){
//與當前排序文件相同,切換排序方向
SortAscending=!SortAscending;
}
ViewState["SortField"]=value;
}
}

//在ViewState中跟蹤SortAscending屬性
boolSortAscending{

get{
objecto=ViewState["SortAscending"];
if(o==null){
returntrue;
}
return(bool)o;
}

set{
ViewState["SortAscending"]=value;
}
}

voidPage_Load(objectsender,EventArgse){

if(!Page.IsPostBack){
BindGrid();
}
}

voidBindGrid(){

//獲取數據
DataSetds=newDataSet();
ds.ReadXml(Server.MapPath("TestData.xml"));

DataViewdv=newDataView(ds.Tables[0]);

//應用排序過濾器和方向
dv.Sort=SortField;
if(!SortAscending){
dv.Sort+="DESC";
}

//綁定網格
DataGrid1.DataSource=dv;
DataGrid1.DataBind();
}

voidSortGrid(objectsender,DataGridSortCommandEventArgse){

DataGrid1.CurrentPageIndex=0;
SortField=e.SortExpression;
BindGrid();
}

</script>

下麵是上述兩個代碼段中引用的testdata.xml的代碼:


<?xmlversion="1.0"standalone="yes"?>
<NewDataSet>
<Table>
<pub_id>0736</pub_id>
<pub_name>NewMoonBooks</pub_name>
<city>Boston</city>
<state>MA</state>
<country>USA</country>
</Table>
<Table>
<pub_id>0877</pub_id>
<pub_name>Binnet&Hardley</pub_name>
<city>Washington</city>
<state>DC</state>
<country>USA</country>
</Table>
<Table>
<pub_id>1389</pub_id>
<pub_name>AlgodataInfosystems</pub_name>
<city>Berkeley</city>
<state>CA</state>
<country>USA</country>
</Table>
<Table>
<pub_id>1622</pub_id>
<pub_name>FiveLakesPublishing</pub_name>
<city>Chicago</city>
<state>IL</state>
<country>USA</country>
</Table>
<Table>
<pub_id>1756</pub_id>
<pub_name>RamonaPublishers</pub_name>
<city>Dallas</city>
<state>TX</state>
<country>USA</country>
</Table>
<Table>
<pub_id>9901</pub_id>
<pub_name>GGG&G</pub_name>
<city>Muenchen</city>
<country>Germany</country>
</Table>
<Table>
<pub_id>9952</pub_id>
<pub_name>ScootneyBooks</pub_name>
<city>NewYork</city>
<state>NY</state>
<country>USA</country>
</Table>
<Table>
<pub_id>9999</pub_id>
<pub_name>LucernePublishing</pub_name>
<city>Paris</city>
<country>France</country>
</Table>
</NewDataSet>

最後更新:2017-04-02 00:06:33

  上一篇:go TreeView使用筆記
  下一篇:go 用ASP.NET開發Web服務的五則技巧