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