自定义属性状态管理示例

更新:2007 年 11 月

此示例演示如何定义一个服务器控件属性,该属性的类型将实现自己的状态管理。该示例中的 BookNew 控件定义一个 Author 属性,其类型 StateManagedAuthor 通过实现 IStateManager 接口执行自己的状态管理。StateManagedAuthor 类型在自定义类型状态管理示例中有描述。

本主题中的 BookNew 控件类似于服务器控件属性示例中描述的 Book 控件,后一主题演示如何定义包含子属性的属性。BookNew 与 Book 控件之间的差异在于,BookNew 中的 Author 属性的类型将 Author 属性的状态管理委托给该属性的 StateManagedAuthor 类型的状态管理方法。相反,Book 控件显式管理其 Author 属性的状态。

用于实现状态管理的方法(TrackViewStateSaveViewStateLoadViewState)在本主题后面的“代码讨论”部分中有描述。

BookNew 控件的代码清单

以下为 BookNew 控件的代码清单。通过 BookNew 的 Author 属性以及 StateManagedAuthor 类的状态管理方法(TrackViewState、SaveViewState 和 LoadViewState)来执行状态管理。

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), _
    DefaultProperty("Title"), _
    ToolboxData("<{0}:BookNew runat=""server""> </{0}:BookNew>") _
    > _
    Public Class BookNew
        Inherits WebControl
        Private authorValue As StateManagedAuthor

        < _
        Bindable(True), _
        Category("Appearance"), _
        DefaultValue(""), _
        Description("The name of the author."), _
        DesignerSerializationVisibility( _
            DesignerSerializationVisibility.Content), _
        PersistenceMode(PersistenceMode.InnerProperty) _
        > _
        Public Overridable ReadOnly Property Author() _
            As StateManagedAuthor
            Get
                If authorValue Is Nothing Then
                    authorValue = New StateManagedAuthor
                    If IsTrackingViewState Then
                        CType(authorValue, IStateManager).TrackViewState()
                    End If
                End If
                Return authorValue
            End Get
        End Property

        < _
        Bindable(True), _
        Category("Appearance"), _
        DefaultValue(BookType.NotDefined), _
        Description("Fiction or Not") _
        > _
        Public Overridable Property BookType() As BookType
            Get
                Dim t As Object = ViewState("BookType")
                If t Is Nothing Then t = BookType.NotDefined
                Return CType(t, BookType)
            End Get
            Set(ByVal value As BookType)
                ViewState("BookType") = value
            End Set
        End Property

        < _
        Bindable(True), _
        Category("Appearance"), _
        DefaultValue(""), _
        Description("The symbol for the currency."), _
        Localizable(True) _
        > _
        Public Overridable Property CurrencySymbol() As String
            Get
                Dim s As String = CStr(ViewState("CurrencySymbol"))
                If s Is Nothing Then s = String.Empty
                Return s
            End Get
            Set(ByVal value As String)
                ViewState("CurrencySymbol") = value
            End Set
        End Property

        < _
         Bindable(True), _
         Category("Appearance"), _
         DefaultValue("0.00"), _
         Description("The price of the book."), _
         Localizable(True) _
         > _
         Public Overridable Property Price() As Decimal
            Get
                Dim p As Object = ViewState("Price")
                If p Is Nothing Then p = Decimal.Zero
                Return CType(p, Decimal)
            End Get
            Set(ByVal value As Decimal)
                ViewState("Price") = value
            End Set
        End Property

        < _
        Bindable(True), _
        Category("Appearance"), _
        DefaultValue(""), _
        Description("The title of the book."), _
        Localizable(True) _
        > _
        Public Overridable Property Title() As String
            Get
                Dim s As String = CStr(ViewState("Title"))
                If s Is Nothing Then s = String.Empty
                Return s
            End Get
            Set(ByVal value As String)
                ViewState("Title") = value
            End Set
        End Property

        Protected Overrides Sub Render(ByVal writer As HtmlTextWriter)
            MyBase.AddAttributesToRender(writer)
            writer.RenderBeginTag(HtmlTextWriterTag.Table)

            writer.RenderBeginTag(HtmlTextWriterTag.Tr)
            writer.RenderBeginTag(HtmlTextWriterTag.Td)
            writer.WriteEncodedText(Title)
            writer.RenderEndTag()
            writer.RenderEndTag()

            writer.RenderBeginTag(HtmlTextWriterTag.Tr)
            writer.RenderBeginTag(HtmlTextWriterTag.Td)
            writer.WriteEncodedText(Author.ToString())
            writer.RenderEndTag()
            writer.RenderEndTag()

            writer.RenderBeginTag(HtmlTextWriterTag.Tr)
            writer.RenderBeginTag(HtmlTextWriterTag.Td)
            writer.WriteEncodedText(BookType.ToString())
            writer.RenderEndTag()
            writer.RenderEndTag()

            writer.RenderBeginTag(HtmlTextWriterTag.Tr)
            writer.RenderBeginTag(HtmlTextWriterTag.Td)
            writer.Write(CurrencySymbol)
            writer.Write("&nbsp")
            writer.Write(String.Format("{0:F2}", Price))
            writer.RenderEndTag()
            writer.RenderEndTag()

            writer.RenderEndTag()
        End Sub

#Region "state management"
        Protected Overrides Sub LoadViewState( _
            ByVal savedState As Object)
            Dim p As Pair = TryCast(savedState, Pair)
            If p IsNot Nothing Then
                MyBase.LoadViewState(p.First)
                CType(Author, IStateManager).LoadViewState(p.Second)
                Return
            End If
            MyBase.LoadViewState(savedState)
        End Sub

        Protected Overrides Function SaveViewState() As Object
            Dim baseState As Object = MyBase.SaveViewState
            Dim thisState As Object = Nothing

            If authorValue IsNot Nothing Then
                thisState = _
                    CType(authorValue, IStateManager).SaveViewState()
            End If

            If thisState IsNot Nothing Then
                Return New Pair(baseState, thisState)
            Else
                Return baseState
            End If
        End Function

        Protected Overrides Sub TrackViewState()
            If authorValue IsNot Nothing Then
                CType(Author, IStateManager).TrackViewState()
            End If
            MyBase.TrackViewState()
        End Sub
#End Region

    End Class
End Namespace
// BookNew.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),
    DefaultProperty("Title"),
    ToolboxData("<{0}:BookNew runat=\"server\"> </{0}:BookNew>")
    ]
    public class BookNew : WebControl
    {
        private StateManagedAuthor authorValue;

        [
        Bindable(true),
        Category("Appearance"),
        DefaultValue(""),
        Description("The name of the author."),
        DesignerSerializationVisibility(
            DesignerSerializationVisibility.Content),
        PersistenceMode(PersistenceMode.InnerProperty)
        ]
        public virtual StateManagedAuthor Author
        {
            get
            {
                if (authorValue == null)
                {
                    authorValue = new StateManagedAuthor();

                    if (IsTrackingViewState)
                    {
                        ((IStateManager)authorValue).TrackViewState();
                    }
                }
                return authorValue;
            }
        }

        [
        Bindable(true),
        Category("Appearance"),
        DefaultValue(BookType.NotDefined),
        Description("Fiction or Not"),
        ]
        public virtual BookType BookType
        {
            get
            {
                object t = ViewState["BookType"];
                return (t == null) ? BookType.NotDefined : (BookType)t;
            }
            set
            {
                ViewState["BookType"] = value;
            }
        }

        [
        Bindable(true),
        Category("Appearance"),
        DefaultValue(""),
        Description("The symbol for the currency."),
        Localizable(true)
        ]
        public virtual string CurrencySymbol
        {
            get
            {
                string s = (string)ViewState["CurrencySymbol"];
                return (s == null) ? String.Empty : s;
            }
            set
            {
                ViewState["CurrencySymbol"] = value;
            }
        }


        [
        Bindable(true),
        Category("Appearance"),
        DefaultValue("0.00"),
        Description("The price of the book."),
        Localizable(true)
        ]
        public virtual Decimal Price
        {
            get
            {
                object price = ViewState["Price"];
                return (price == null) ? Decimal.Zero : (Decimal)price;
            }
            set
            {
                ViewState["Price"] = value;
            }
        }

        [
        Bindable(true),
        Category("Appearance"),
        DefaultValue(""),
        Description("The title of the book."),
        Localizable(true)
        ]
        public virtual string Title
        {
            get
            {
                string s = (string)ViewState["Title"];
                return (s == null) ? String.Empty : s;
            }
            set
            {
                ViewState["Title"] = value;
            }
        }

        protected override void Render(HtmlTextWriter writer)
        {
            base.AddAttributesToRender(writer);
            writer.RenderBeginTag(HtmlTextWriterTag.Table);

            writer.RenderBeginTag(HtmlTextWriterTag.Tr);
            writer.RenderBeginTag(HtmlTextWriterTag.Td);
            writer.WriteEncodedText(Title);
            writer.RenderEndTag();
            writer.RenderEndTag();

            writer.RenderBeginTag(HtmlTextWriterTag.Tr);
            writer.RenderBeginTag(HtmlTextWriterTag.Td);
            writer.WriteEncodedText(Author.ToString());
            writer.RenderEndTag();
            writer.RenderEndTag();

            writer.RenderBeginTag(HtmlTextWriterTag.Tr);
            writer.RenderBeginTag(HtmlTextWriterTag.Td);
            writer.WriteEncodedText(BookType.ToString());
            writer.RenderEndTag();
            writer.RenderEndTag();

            writer.RenderBeginTag(HtmlTextWriterTag.Tr);
            writer.RenderBeginTag(HtmlTextWriterTag.Td);
            writer.Write(CurrencySymbol);
            writer.Write("&nbsp;");
            writer.Write(String.Format("{0:F2}", Price));
            writer.RenderEndTag();
            writer.RenderEndTag();

            writer.RenderEndTag();
        }

        #region state management
        protected override void LoadViewState(object savedState)
        {
            Pair p = savedState as Pair;
            if (p != null)
            {
                base.LoadViewState(p.First);
                ((IStateManager)Author).LoadViewState(p.Second);
                return;
            }
            base.LoadViewState(savedState);
        }

        protected override object SaveViewState()
        {
            object baseState = base.SaveViewState();
            object thisState = null;

            if (authorValue != null)
            {
                thisState = ((IStateManager)authorValue).SaveViewState();
            }

            if (thisState != null)
            {
                return new Pair(baseState, thisState);
            }
            else
            {
                return baseState;
            }

        }

        protected override void TrackViewState()
        {
            if (authorValue != null)
            {
                ((IStateManager)authorValue).TrackViewState();
            }
            base.TrackViewState();
        }
        #endregion

    }
}

代码讨论

BookNew 控件演示通过实现 IStateManager 接口来实现其类型管理自身状态的属性的模式。研究本主题中的 BookNew 控件的代码时,您可能会发现同时了解一下 StateManagedAuthor 类型(即 BookNew 控件的 Author 属性的类型)的代码会非常有用。StateManagedAuthor 的代码清单位于自定义类型状态管理示例主题中。

BookNew 将 Author 属性定义为只读属性,该属性存储于 StateManagedAuthor 类型的私有字段中。在 Author 属性访问器中,如果与该属性对应的私有字段为 null(在 Visual Basic 中为 Nothing),则 BookNew 会将一个新的 StateManagedAuthor 对象分配给该私有字段。如果 BookNew 已启动状态跟踪,则 BookNew 会通过调用新创建的 StateManagedAuthor 对象的 TrackViewState 方法对该对象启动状态跟踪。有关跟踪的更多信息,请参见服务器控件属性示例

Author 属性通过从其自己的状态管理方法(TrackViewStateSaveViewStateLoadViewState)中调用 StateManagedAuthor 对象的 IStateManager 方法来参与状态管理。

在重写的 TrackViewState 方法中,BookNew 调用基类的 TrackViewState 方法以及与 Author 属性对应的 StateManagedAuthor 对象的 TrackViewState 方法。

在重写的 SaveViewState 方法中,BookNew 调用基类的 SaveViewState 方法以及与 Author 属性对应的 StateManagedAuthor 对象的 SaveViewState 方法。如果 Author 属性有要保存的状态,则 BookNew 控件的 SaveViewState 方法将返回一个 Pair 对象,该对象包含基类和 Author 属性的状态。如果 Author 属性没有状态要保存,则该方法仅返回由对基类调用 SaveViewState 返回的状态。基于提供状态的自定义属性的数量,应从 SaveViewState 中返回 PairTripletArray 类型的对象。这样便可以采用 LoadViewState 方法更容易地检索具有已保存状态的不同部件。在这种情况下,因为存在两个项,即基类状态和 Author 状态,故而将使用 Pair 类。

在重写的 LoadViewState 方法中,BookNew 实现其在 LoadViewState 方法中实现的操作的反向操作。BookNew 将状态加载到基类和 Author 属性中,或者如果 Author 属性没有状态要保存,则 BookNew 仅会将状态加载到基类中。即使保存的状态为 null(在 Visual Basic 中为 Nothing),也应始终调用基类的 LoadViewState 方法,因为基类在没有状态要还原时可能已经在此方法中实现了其他逻辑。

BookNew 控件的测试页

下面的示例演示一个使用 BookNew 控件的 .aspx 页。

<%@ Page Language="C#"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<script runat="server">
  void Button_Click(object sender, EventArgs e)
  {
    BookNew1.Author.FirstName = "Bob";
    BookNew1.Author.LastName = "Kelly";
    BookNew1.Title = "Contoso Stories";
    BookNew1.Price = 39.95M;
    Button1.Visible = false;
  }  
</script>
<html xmlns="http://www.w3.org/1999/xhtml" >
  <head id="Head1" runat="server">
    <title>
      BookNew test page
    </title>
  </head>
  <body>
    <form id="Form1" runat="server">
      <aspSample:BookNew ID="BookNew1" Runat="server" 
        BorderStyle="Solid" BorderWidth="1px" Title="Tailspin Stories"
        CurrencySymbol="$" BackColor="#FFE0C0" Font-Names="Tahoma" 
        Price="16" BookType="Fiction">
        <Author FirstName="Judy" LastName="Lew" />
      </aspSample:BookNew>
      <br />
      <asp:Button ID="Button1" OnClick="Button_Click" 
        Runat="server" Text="Change" />
      <asp:Button ID="Button2" Runat="server" Text="Refresh" />
      <br />
      <br />
      <asp:HyperLink ID="Hyperlink1" href="BookNewTest.aspx" 
        Runat="server">
        Reload Page</asp:HyperLink>
    </form>
  </body>
</html>

生成和使用示例

使用自定义类型状态管理示例中描述的 StateManagedAuthor 类和 StateManagedAuthorConverter 类编译 BookNew 控件。

有关生成控件并在页中使用该控件的更多信息,请参见生成自定义服务器控件示例

请参见

概念

自定义类型状态管理示例

服务器控件属性示例

其他资源

开发自定义 ASP.NET 服务器控件