基本概念
控件狀態(tài)-為了讓控件正常工作,有時需要存儲控件狀態(tài)數(shù)據(jù)。例如,如果編寫了一個自定義控件,其中具有顯示不同信息的不同選項卡,為使該控件如預(yù)期一樣工作,控件需要知道在往返過程中選擇的是哪個選項卡。ViewState 屬性可用于此目的,但開發(fā)人員可能在頁級別關(guān)閉了視圖狀態(tài),從而有效地中斷控件。為解決此問題,asp.net 頁框架在 ASP.NET 2.0 版中公開了一種稱為控件狀態(tài)的新功能。
ControlState 屬性允許保持特定于控件的屬性信息,不像 ViewState 屬性一樣可以關(guān)閉。若要使用控件狀態(tài),控件必須在初始化過程中調(diào)用 RegisterRequiresControlState 方法,然后重寫 SaveControlState 和 LoadControlState 方法。
視圖狀態(tài)-視圖狀態(tài)是 ASP.NET 頁框架默認(rèn)情況下用于保存往返過程之間的頁和控件值的方法。當(dāng)呈現(xiàn)頁的 HTML 形式時,需要在回發(fā)過程中保留的頁的當(dāng)前狀態(tài)和值將被序列化為 Base64 編碼的字符串,并輸出到視圖狀態(tài)的隱藏字段中。通過實現(xiàn)自定義的 PageStatePersister 類以存儲頁數(shù)據(jù),您可以更改默認(rèn)行為并將視圖狀態(tài)存儲到另一個位置(如 SQL Server 數(shù)據(jù)庫)。有關(guān)將頁狀態(tài)存儲到流上而不是隱藏的頁字段中的示例,請參見 視圖狀態(tài)持久性機制的示例。
您可以通過使用頁的 ViewState 屬性將往返過程中的數(shù)據(jù)保存到 Web 服務(wù)器來利用自己的代碼訪問視圖狀態(tài)。ViewState 屬性是一個包含密鑰/值對(其中包含視圖狀態(tài)數(shù)據(jù))的字典。
各自的優(yōu)勢與劣勢
視圖狀態(tài)
使用視圖狀態(tài)的優(yōu)點:
·不需要任何服務(wù)器資源 視圖狀態(tài)包含在頁代碼內(nèi)的結(jié)構(gòu)中。
·實現(xiàn)簡單 視圖狀態(tài)無需使用任何自定義編程。默認(rèn)情況下對控件啟用狀態(tài)數(shù)據(jù)的維護(hù)。
·增強的安全功能 視圖狀態(tài)中的值經(jīng)過哈希計算和壓縮,并且針對 Unicode 實現(xiàn)進(jìn)行編碼,其安全性要高于使用隱藏域。
使用視圖狀態(tài)的缺點
·性能注意事項 由于視圖狀態(tài)存儲在頁本身,因此如果存儲較大的值,用戶顯示頁和發(fā)送頁時的速度可能會減慢。尤其是對移動設(shè)備,其帶寬通常是有限的。
·設(shè)備限制 移動設(shè)備可能沒有足夠的內(nèi)存容量來存儲大量的視圖狀態(tài)數(shù)據(jù)。
·潛在的安全風(fēng)險 視圖狀態(tài)存儲在頁上的一個或多個隱藏域中。雖然視圖狀態(tài)以哈希格式存儲數(shù)據(jù),但它可以被篡改。如果直接查看頁輸出源,可以看到隱藏域中的信息,這導(dǎo)致潛在的安全性問題。
控件狀態(tài)
使用控件狀態(tài)的優(yōu)點:
·不需要任何服務(wù)器資源 默認(rèn)情況下,控件狀態(tài)存儲在頁上的隱藏域中。
·可靠性 因為控件狀態(tài)不像視圖狀態(tài)那樣可以關(guān)閉,控件狀態(tài)是管理控件的狀態(tài)的更可靠方法。
·通用性 可以編寫自定義適配器來控制如何存儲控件狀態(tài)數(shù)據(jù)和控件狀態(tài)數(shù)據(jù)的存儲位置。
使用控件狀態(tài)的缺點:
·需要一些編程 雖然 ASP.NET 頁框架為控件狀態(tài)提供了基礎(chǔ),但是控件狀態(tài)是一個自定義的狀態(tài)保持機制。為了充分利用控件狀態(tài),您必須編寫代碼來保存和加載控件狀態(tài)。
控件狀態(tài)與視圖狀態(tài)示例
此示例演示如何創(chuàng)建一個名為 IndexButton 的自定義控件,該控件使用控件狀態(tài)在多個頁請求間維護(hù)關(guān)鍵狀態(tài)信息。在 ASP.NET 2.0 版中引入的控件狀態(tài)與視圖狀態(tài)類似,但功能上獨立于視圖狀態(tài)。網(wǎng)頁開發(fā)人員可能會出于性能原因而禁用整個頁面或單個控件的視圖狀態(tài),但他們不能禁用控件狀態(tài)。控件狀態(tài)是專為存儲控件的重要數(shù)據(jù)(如一個頁面控件的頁數(shù))而設(shè)計的,回發(fā)時必須用到這些數(shù)據(jù)才能使控件正常工作(即便禁用視圖狀態(tài)也不受影響)。默認(rèn)情況下,ASP.NET 頁框架將控件狀態(tài)存儲在頁的一個隱藏元素中,視圖狀態(tài)也同樣存儲在此隱藏元素中。即使禁用視圖狀態(tài),或是使用 session 管理狀態(tài)時,頁面中的控件狀態(tài)仍會傳輸至客戶端,然后返回到服務(wù)器。在回發(fā)時,ASP.NET 會對隱藏元素的內(nèi)容進(jìn)行反序列化,并將控件狀態(tài)加載到每個注冊過控件狀態(tài)的控件中。
此示例闡釋了一個同時在控件狀態(tài)和視圖狀態(tài)中保存狀態(tài)的自定義控件。在此示例中,IndexButton 控件派生自 Button 類,還定義了一個 Index 屬性,并將該屬性保存在控件狀態(tài)中。為了進(jìn)行比較,IndexButton 還定義了一個 IndexInViewState 屬性,該屬性存儲在 ViewState 字典中。為了了解控件狀態(tài)和視圖狀態(tài)之間的差異,請使用本文附帶的程序來演示 IndexButton 控件。
IndexButton控件源碼
using System;
using System.ComponentModel;
using System.Security.Permissions;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace CustomerControls
{
[
AspNetHostingPermission(SecurityAction.Demand, Level = AspNetHostingPermissionLevel.Minimal),
AspNetHostingPermission(SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal),
ToolboxData("<{0}:IndexButton runat=/"server/"> </{0}:IndexButton>")
]
public class IndexButton : Button
{
PRivate int indexValue;
[
Bindable(true),
Category("Behavior"),
DefaultValue(0),
Descr ]
public int Index
{
get
{
return indexValue;
}
set
{
indexValue = value;
}
}
[
Bindable(true),
Category("Behavior"),
DefaultValue(0),
Description("The index stored in view state.")
]
public int IndexInViewState
{
get
{
object obj = ViewState["IndexInViewState"];
return (obj == null) ? 0 : (int)obj;
}
set
{
ViewState["IndexInViewState"] = value;
}
}
protected override void OnInit(EventArgs e)
{
base.OnInit(e);
Page.RegisterRequiresControlState(this);
}
protected override object SaveControlState()
{
//調(diào)用基類的方法,從基類得到控件狀態(tài)的基值
//如果indexValue不等于并且基類的控件狀態(tài)不為null
//使用Pair作為便利的數(shù)據(jù)結(jié)構(gòu)來高效保存(和在LoadControlState方法中還原)
//由兩部分組成的控件狀態(tài)
object obj = base.SaveControlState();
if (indexValue != 0)
{
if (obj != null)
{
return new Pair(obj, indexValue);
}
else
{
return (indexValue);
}
}
else
{
return obj;
}
}
protected override void LoadControlState(object state)
{
if (state != null)
{
Pair p = state as Pair;
if (p != null)
{
base.LoadControlState(p.First);
indexValue = (int)p.Second;
}
else
{
if (state is int)
{
indexValue = (int)state;
}
else
{
base.LoadControlState(state);
}
}
}
}
}
}
代碼討論
IndexButton 控件的實現(xiàn)闡釋了三個任務(wù),必須執(zhí)行這三個任務(wù)才能使控件參與控件狀態(tài):
· 重寫 OnInit 方法并調(diào)用 RegisterRequiresControlState 方法向頁面注冊,以參與控件狀態(tài)。必須針對每個請求完成此任務(wù)。
· 重寫 SaveControlState 方法,以在控件狀態(tài)中保存數(shù)據(jù)。
· 重寫 LoadControlState 方法,以從控件狀態(tài)加載數(shù)據(jù)。此方法調(diào)用基類方法,并獲取基類對控件狀態(tài)的基值。如果 indexValue 字段不為零,而且基類的控件狀態(tài)也不為空,Pair 類便可作為方便的數(shù)據(jù)結(jié)構(gòu)使用,用來保存和還原由兩部分組成的控件狀態(tài)。
分析總結(jié)
從MSDN上的一系列的技術(shù)參考來看,ControlState應(yīng)該是主要在自定義控件上使用,“ASP.NET 頁框架提供了 ControlState 屬性作為在服務(wù)器往返過程中存儲自定義控件數(shù)據(jù)的方法”,這是MSDN上的原句,ASP.NET2.0只是為ControlState提供了一個基礎(chǔ),當(dāng)ControlState是一個自定義的狀態(tài)保持機制,也就是說保持狀態(tài)的機制需要你開發(fā)人員自己去完成,而不像ViewState,它有自己默認(rèn)的狀態(tài)保持機制。在自定義控件使用ControlState也許才是微軟本意了,為的就避免在頁面級別禁用掉ViewState后,自定義控件還能正常運行。當(dāng)然這里的意思就是,某些控件的正確運行是依賴于它的狀態(tài)信息的,在ASP.NET1.1中,如果禁用了ViewState,這樣的控件就無法正確運行了。但引入了ControlState后就不同了,因為ControlState是禁用不掉的。
所以微軟才提醒開發(fā)人員“請僅對那些在回發(fā)過程中對控件至關(guān)重要的少量關(guān)鍵數(shù)據(jù)使用控件狀態(tài),而不要將控件狀態(tài)作為視圖狀態(tài)的備用選項使用”。明確說出,ControlState和ViewState完全是兩個東西,雖然它們可以完成相同的任務(wù),新推出的ControlState既不是用來替代ViewState也不是用來做ViewState的替補。它的使命是彌補ViewState的所不能完成的任務(wù),讓開發(fā)人員開發(fā)出更加健壯的控件。例如說,開發(fā)的自定義控件某個狀態(tài)是至關(guān)重要的,缺少它就自定義控件不能正常工作,那么ControlState就該上場了。而且ControlState是自定義的狀態(tài)保持機制,也限制了ControlState自由的使用,你不但要在OnInit 方法并調(diào)用 RegisterRequiresControlState 方法向頁面注冊,而且要重寫SaveAdapterControlState(),LoadAdapterControlState(object state)兩個方法自己去實現(xiàn)要保存什么,怎樣保存。根據(jù)我現(xiàn)在的理解,如果你需要保存該控件的10種不同狀態(tài),那你就得一一保存,再一一加載上去。從這點也就看出了微軟的初衷了,那不是很明顯嗎,如果不需要ControlState那就不使用它吧,否則怎么它什么都讓我們開發(fā)人員去做呢?
這只是基礎(chǔ)了,剛才我說了,似乎微軟也是這么說的,ControlState針對的是自定義控件,其實我們真的要去開啟基本控件例如Label控件的ControlState,微軟也是允許的,這就是稍深的內(nèi)容了,這就涉及到控件適配器了(ControlAdapter)。如果需要了解這方面的內(nèi)容,請看用控件適配器開啟基本控件的ControlState。http://sifang2004.VEVb.com/archive/2006/06/01/415288.html
附錄
為了更加充分理解上面的內(nèi)容,需要對以下內(nèi)容有個了解:
Pair 類
用作存儲兩個相關(guān)對象的基本結(jié)構(gòu)。它是在整個 ASP.NET 中(在如頁面狀態(tài)管理任務(wù)期間或配置節(jié)處理程序的過程中)有多種用法的實用工具類。可以在自己的代碼中需要包含兩個相關(guān)對象的結(jié)構(gòu)的任意位置和不一定需要數(shù)據(jù)綁定的位置使用 Pair 類。Pair 類不將其對象引用 First 和 Second 封裝在屬性中;該類直接將它們作為公共類字段公開到所有調(diào)用代碼。
Pair 類在頁狀態(tài)保留實現(xiàn)中有多種用法。最常見的用法是同時作為 ViewState 和 ControlState 集合的容器。在這種情況下,F(xiàn)irst 屬性用于 ViewState,而 Second 用于 ControlState。
PageStatePersister 類
HTTP 請求和響應(yīng)原本是無狀態(tài)的。要在 HTTP 請求之間保持狀態(tài)信息,ASP.NET 服務(wù)器頁可以存儲 Page 狀態(tài)。此狀態(tài)稱為視圖狀態(tài),它包含頁和控件設(shè)置及數(shù)據(jù),這些設(shè)置和數(shù)據(jù)使得頁和控件看起來就像在上一次將它們提交到服務(wù)器然后又返回到客戶端時,用戶所看到并與之交互的頁和控件一樣。有幾種機制可在對相同頁的連續(xù)請求之間存儲視圖狀態(tài)。PageStatePersister 抽象類表示這些狀態(tài)信息存儲機制的基類。
要在不能支持現(xiàn)有視圖狀態(tài)持久性機制的客戶端上保留視圖狀態(tài),可以擴展 PageStatePersister 類,引入您自己的視圖狀態(tài)持久性方法,并且可以使用頁適配器將 ASP.NET 應(yīng)用程序配置為根據(jù)為其提供頁的客戶端的類型使用不同的視圖狀態(tài)持久性機制。從 PageStatePersister 類派生的類必須重寫 Save 抽象方法,以便在持久性介質(zhì)中存儲視圖狀態(tài)和控件狀態(tài),同時重寫 Load 方法以提取狀態(tài)信息。如果想知道如何寫PageStatePersister的派生類,請參考視圖狀態(tài)持久性機制。
新聞熱點
疑難解答