此示例演示如何创建一个名为 IndexButton 的自定义控件,该控件使用控件状态在多个页请求间维护关键状态信息。在 ASP.NET 2.0 版中引入的控件状态与视图状态类似,但功能上独立于视图状态。网页开发人员可能会出于性能原因而禁用整个页面或单个控件的视图状态,但他们不能禁用控件状态。控件状态是专为存储控件的重要数据(如一个页面控件的页数)而设计的,回发时必须用到这些数据才能使控件正常工作(即便禁用视图状态也不受影响)。默认情况下,ASP.NET 页框架将控件状态存储在页的一个隐藏元素中,视图状态也同样存储在此隐藏元素中。即使禁用视图状态,或是使用 Session 管理状态时,页面中的控件状态仍会传输至客户端,然后返回到服务器。在回发时,ASP.NET 会对隐藏元素的内容进行反序列化,并将控件状态加载到每个注册过控件状态的控件中。
注意 |
---|
请仅对那些在回发过程中对控件至关重要的少量关键数据使用控件状态,而不要将控件状态作为视图状态的备用选项使用。 |
此示例阐释了一个同时在控件状态和视图状态中保存状态的自定义控件。在此示例中,IndexButton 控件派生自 Button 类,还定义了一个 Index 属性,并将该属性保存在控件状态中。为了进行比较,IndexButton 还定义了一个 IndexInViewState 属性,该属性存储在 ViewState 字典中。为了了解控件状态和视图状态之间的差异,请使用本主题后面的“IndexButton 控件的测试页”一节中列出的 .aspx 页来演示 IndexButton 控件。
IndexButton 控件的代码清单
' IndexButton.vb Option Strict On Imports System Imports System.ComponentModel Imports System.Security.Permissions Imports System.Web Imports System.Web.UI Imports System.Web.UI.WebControls Namespace Samples.AspNet.VB.Controls < _ AspNetHostingPermission(SecurityAction.Demand, _ Level:=AspNetHostingPermissionLevel.Minimal), _ AspNetHostingPermission(SecurityAction.InheritanceDemand, _ Level:=AspNetHostingPermissionLevel.Minimal), _ ToolboxData("<{0}:IndexButton runat=""server""> </{0}:IndexButton>") _ > _ Public Class IndexButton Inherits Button Private indexValue As Integer < _ Bindable(True), _ Category("Behavior"), _ DefaultValue(0), _ Description("The index stored in control state.") _ > _ Public Property Index() As Integer Get Return indexValue End Get Set(ByVal value As Integer) indexValue = value End Set End Property < _ Bindable(True), _ Category("Behavior"), _ DefaultValue(0), _ Description("The index stored in view state.") _ > _ Public Property IndexInViewState() As Integer Get Dim obj As Object = ViewState("IndexInViewState") If obj Is Nothing Then obj = 0 Return CInt(obj) End Get Set(ByVal value As Integer) ViewState("IndexInViewState") = value End Set End Property Protected Overrides Sub OnInit(ByVal e As EventArgs) MyBase.OnInit(e) Page.RegisterRequiresControlState(Me) End Sub Protected Overrides Function SaveControlState() As Object ' Invoke the base class's method and ' get the contribution to control state ' from the base class. ' If the indexValue field is not zero ' and the base class's control state is not null, ' use Pair as a convenient data structure ' to efficiently save ' (and restore in LoadControlState) ' the two-part control state ' and restore it in LoadControlState. Dim obj As Object = MyBase.SaveControlState() If indexValue <> 0 Then If obj IsNot Nothing Then Return New Pair(obj, indexValue) Else Return indexValue End If Else Return obj End If End Function Protected Overrides Sub LoadControlState(ByVal state As Object) If (state IsNot Nothing) Then Dim p As Pair = TryCast(state, Pair) If p IsNot Nothing Then MyBase.LoadControlState(p.First) indexValue = CInt(p.Second) Else If (TypeOf (state) Is Integer) Then indexValue = CInt(state) Else MyBase.LoadControlState(state) End If End If End If End Sub End Class End Namespace
// IndexButton.cs using System; using System.ComponentModel; using System.Security.Permissions; using System.Web; using System.Web.UI; using System.Web.UI.WebControls; namespace Samples.AspNet.CS.Controls { [ 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() { // Invoke the base class's method and // get the contribution to control state // from the base class. // If the indexValue field is not zero // and the base class's control state is not null, // use Pair as a convenient data structure // to efficiently save // (and restore in LoadControlState) // the two-part control state // and restore it in 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 类便可作为方便的数据结构使用,用来保存和还原由两部分组成的控件状态。
IndexButton 控件的测试页
下面的示例阐释一个页面,该页通过在 @ Page 指令中将 EnableViewState 属性设置为 false 来禁用视图状态。该页使用 IndexButton 控件,并在 Page_Load 事件处理程序中,将控件的 Index 和 IndexInViewState 属性的值加 1。页中的标签显示 Index 和 IndexInViewState 两个属性的值。
由于 Index 属性存储在无法禁用的控件状态中,因而 Index 属性会在回发时维护其值,并在每次将页回发到服务器时加 1。相比之下,因为 IndexInViewState 属性存储在视图状态中,而页的视图状态已被禁用,所以 IndexInViewState 属性始终为默认值零。
<%@ Page Language="VB" Trace="true" EnableViewState="false" %> <script runat="server"> Sub Page_Load(ByVal sender As Object, ByVal e As EventArgs) Label1.Text = IndexButton1.Index.ToString() Label2.Text = IndexButton1.IndexInViewState.ToString() IndexButton1.Index += 1 IndexButton1.IndexInViewState += 1 End Sub </script> <html> <head id="Head1" runat="server"> <title>IndexButton test page</title> </head> <body> <form id="form1" runat="server"> Click the button: <aspSample:IndexButton Text="IndexButton" ID="IndexButton1" runat="server"/> <br /> <br /> The value of the Index property of IndexButton is:<br /> <asp:Label ID="Label1" Runat="server" Text="Label"> </asp:Label> <br /> <br /> The value of the IndexInViewState property of IndexButton is: <br /> <asp:Label ID="Label2" Runat="server" Text="Label"> </asp:Label> <br /> </form> </body> </html>
<%@ Page Language="C#" Trace="true" EnableViewState="false" %> <script runat="server"> void Page_Load(object sender, EventArgs e) { Label1.Text = (IndexButton1.Index++).ToString(); Label2.Text = (IndexButton1.IndexInViewState++).ToString(); } </script> <html> <head id="Head1" runat="server"> <title>IndexButton test page</title> </head> <body> <form id="form1" runat="server"> Click the button: <aspSample:IndexButton Text="IndexButton" ID="IndexButton1" runat="server"/> <br /> <br /> The value of the Index property of IndexButton is:<br /> <asp:Label ID="Label1" Runat="server" Text="Label"> </asp:Label> <br /> <br /> The value of the IndexInViewState property of IndexButton is: <br /> <asp:Label ID="Label2" Runat="server" Text="Label"> </asp:Label> <br /> </form> </body> </html>