基本概念
控制項狀態-為了讓控制項正常工作,有時需要儲存控制項狀態資料。例如,如果編寫了一個自訂控制項,其中具有顯示不同資訊的不同選項卡,為使該控制項如預期一樣工作,控制項需要知道在往返過程中選擇的是哪個選項卡。ViewState 屬性可用於此目的,但開發人員可能在頁層級關閉了檢視狀態,從而有效地中斷控制項。為解決此問題,ASP.NET 頁架構在 ASP.NET 2.0 版中公開了一種稱為控制項狀態的新功能。
ControlState 屬性允許保持特定於控制項的屬性資訊,不像 ViewState 屬性一樣可以關閉。若要使用控制項狀態,控制項必須在初始化過程中調用 RegisterRequiresControlState 方法,然後重寫 SaveControlState 和 LoadControlState 方法。
檢視狀態-檢視狀態是 ASP.NET 頁架構預設情況下用於儲存往返過程之間的頁和控制項值的方法。當呈現頁的 HTML 形式時,需要在回傳過程中保留的頁的目前狀態和值將被序列化為 Base 64 編碼的字串,並輸出到檢視狀態的隱藏欄位中。通過實現自訂的 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
{
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()
{
//調用基類的方法,從基類得到控制項狀態的基值
//如果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);
}
}
}
}
}
}