共用方式為


自訂型別狀態管理範例

更新:2007 年 11 月

本範例會示範如何,透過實作 IStateManager 介面讓複雜屬性 (其型別是本身具有屬性的類別) 參與 ASP.NET 檢視狀態。如伺服器控制項屬性範例所述,您可以使用控制項的 ViewState 屬性管理簡單屬性的狀態。但是,若要對集合屬性或具有子屬性的屬性提供狀態管理,您必須將屬性當做唯讀屬性實作,以及當做通常實作 IStateManager 的屬性型別一部分實作。

這個範例中定義的 StateManagedAuthor 型別,示範了如何實作 IStateManager。StateManagedAuthor 類別是本身具有子屬性 (例如 FirstName 和 LastName) 的複雜型別。自訂屬性狀態管理範例中描述的 BookNew 控制項 Author 屬性,其型別為 StateManagedAuthor。

StateManagedAuthor 中 IStateManager 介面的實作會在本主題稍後的「程式碼討論」章節中詳細說明。

StateManagedAuthor 類別的程式碼清單

' StateManagedAuthor.vb
Option Strict On
Imports System
Imports System.Collections
Imports System.ComponentModel
Imports System.Globalization
Imports System.Web.UI

Namespace Samples.AspNet.VB.Controls
    < _
    TypeConverter(GetType(StateManagedAuthorConverter)) _
    > _
    Public Class StateManagedAuthor
        Implements IStateManager
        Private isTrackingViewStateValue As Boolean
        Private viewStateValue As StateBag

        Public Sub New()
            Me.New(String.Empty, String.Empty, String.Empty)
        End Sub

        Public Sub New(ByVal first As String, ByVal last As String)
            Me.New(first, String.Empty, last)
        End Sub

        Public Sub New(ByVal first As String, ByVal middle As String, _
            ByVal last As String)
            FirstName = first
            MiddleName = middle
            LastName = last
        End Sub

        < _
        Category("Behavior"), _
        DefaultValue(""), _
        Description("First name of author"), _
        NotifyParentProperty(True) _
        > _
        Public Overridable Property FirstName() As String
            Get
                Dim s As String = CStr(ViewState("FirstName"))
                If s Is Nothing Then s = String.Empty
                Return s
            End Get
            Set(ByVal value As String)
                ViewState("FirstName") = value
            End Set
        End Property

        < _
        Category("Behavior"), _
        DefaultValue(""), _
        Description("Last name of author"), _
        NotifyParentProperty(True) _
        > _
        Public Overridable Property LastName() As String
            Get
                Dim s As String = CStr(ViewState("LastName"))
                If s Is Nothing Then s = String.Empty
                Return s
            End Get
            Set(ByVal value As String)
                ViewState("LastName") = value
            End Set
        End Property

        < _
        Category("Behavior"), _
        DefaultValue(""), _
        Description("Middle name of author"), _
        NotifyParentProperty(True) _
        > _
        Public Overridable Property MiddleName() As String
            Get
                Dim s As String = CStr(ViewState("MiddleName"))
                If s Is Nothing Then s = String.Empty
                Return s
            End Get
            Set(ByVal value As String)
                ViewState("MiddleName") = value
            End Set
        End Property

        Protected Overridable ReadOnly Property ViewState() _
            As StateBag
            Get
                If viewStateValue Is Nothing Then
                    viewStateValue = New StateBag(False)

                    If isTrackingViewStateValue Then
                        CType(viewStateValue, _
                        IStateManager).TrackViewState()
                    End If
                End If
                Return viewStateValue
            End Get
        End Property

        Public Overrides Function ToString() As String
            Return ToString(CultureInfo.InvariantCulture)
        End Function


        Public Overloads Function ToString( _
        ByVal culture As CultureInfo) As String
            Return TypeDescriptor.GetConverter( _
                Me.GetType()).ConvertToString(Nothing, culture, Me)
        End Function

#Region "IStateManager implementation"
        Public ReadOnly Property IsTrackingViewState() As Boolean _
            Implements IStateManager.IsTrackingViewState
            Get
                Return isTrackingViewStateValue
            End Get
        End Property

        Public Sub LoadViewState(ByVal savedState As Object) _
            Implements IStateManager.LoadViewState
            If savedState IsNot Nothing Then
                CType(ViewState, _
                    IStateManager).LoadViewState(savedState)
            End If
        End Sub

        Public Function SaveViewState() As Object _
            Implements IStateManager.SaveViewState
            Dim savedState As Object = Nothing
            If viewStateValue IsNot Nothing Then
                savedState = CType(viewStateValue, _
                    IStateManager).SaveViewState
            End If
            Return savedState
        End Function

        Public Sub TrackViewState() _
            Implements IStateManager.TrackViewState
            isTrackingViewStateValue = True
            If viewStateValue IsNot Nothing Then
                CType(viewStateValue, IStateManager).TrackViewState()
            End If
        End Sub
#End Region

        Protected Sub SetDirty()
            viewStateValue.SetDirty(True)
        End Sub
    End Class
End Namespace
// StateManagedAuthor.cs
using System;
using System.Collections;
using System.ComponentModel;
using System.Globalization;
using System.Web.UI;

namespace Samples.AspNet.CS.Controls
{
    [
    TypeConverter(typeof(StateManagedAuthorConverter))
    ]
    public class StateManagedAuthor : IStateManager
    {
        private bool _isTrackingViewState;
        private StateBag _viewState;

        public StateManagedAuthor()
            :
            this(String.Empty, String.Empty, String.Empty)
        {
        }

        public StateManagedAuthor(string first, string last)
            :
            this(first, String.Empty, last)
        {
        }

        public StateManagedAuthor(string first, string middle, string last)
        {
            FirstName = first;
            MiddleName = middle;
            LastName = last;
        }

        [
        Category("Behavior"),
        DefaultValue(""),
        Description("First name of author"),
        NotifyParentProperty(true)
        ]
        public virtual String FirstName
        {
            get
            {
                string s = (string)ViewState["FirstName"];
                return (s == null) ? String.Empty : s;
            }
            set
            {
                ViewState["FirstName"] = value;
            }
        }

        [
        Category("Behavior"),
        DefaultValue(""),
        Description("Last name of author"),
        NotifyParentProperty(true)
        ]
        public virtual String LastName
        {
            get
            {
                string s = (string)ViewState["LastName"];
                return (s == null) ? String.Empty : s;
            }
            set
            {
                ViewState["LastName"] = value;
            }
        }

        [
        Category("Behavior"),
        DefaultValue(""),
        Description("Middle name of author"),
        NotifyParentProperty(true)
        ]
        public virtual String MiddleName
        {
            get
            {
                string s = (string)ViewState["MiddleName"];
                return (s == null) ? String.Empty : s;
            }
            set
            {
                ViewState["MiddleName"] = value;
            }
        }

        protected virtual StateBag ViewState
        {
            get
            {
                if (_viewState == null)
                {
                    _viewState = new StateBag(false);

                    if (_isTrackingViewState)
                    {
                        ((IStateManager)_viewState).TrackViewState();
                    }
                }
                return _viewState;
            }
        }


        public override string ToString()
        {
            return ToString(CultureInfo.InvariantCulture);
        }

        public string ToString(CultureInfo culture)
        {
            return TypeDescriptor.GetConverter(
                GetType()).ConvertToString(null, culture, this);
        }

        #region IStateManager implementation
        bool IStateManager.IsTrackingViewState
        {
            get
            {
                return _isTrackingViewState;
            }
        }

        void IStateManager.LoadViewState(object savedState)
        {
            if (savedState != null)
            {
                ((IStateManager)ViewState).LoadViewState(savedState);
            }
        }

        object IStateManager.SaveViewState()
        {
            object savedState = null;

            if (_viewState != null)
            {
                savedState =
                   ((IStateManager)_viewState).SaveViewState();
            }
            return savedState;
        }

        void IStateManager.TrackViewState()
        {
            _isTrackingViewState = true;

            if (_viewState != null)
            {
                ((IStateManager)_viewState).TrackViewState();
            }
        }
        #endregion

        internal void SetDirty()
        {
            _viewState.SetDirty(true);
        }
    }
}

程式碼討論

StateManagedAuthor 類別說明了實作一般由 ASP.NET 複雜型別所使用之 IStateManager 介面的模式。StateManagedAuthor 會定義名為 ViewState 的屬性 (存放在型別 StateBag 的私用變數中),以及名為 viewState 的屬性 (在 C# 中) 和 viewStateValue 屬性 (在 Visual Basic 中)。ViewState 屬性會模擬 Control 類別的 ViewState 屬性。StateManagedAuthor 會將屬性存放在 ViewState 字典中,正如控制項將其簡單屬性存放在 Control 類別的 ViewState 字典中。

IStateManager 介面具有一個 IsTrackingViewState 屬性和三個方法:TrackViewStateSaveViewStateLoadViewStateIStateManager 介面的成員和 Control 類別中對應的方法具有相同的語意 (Semantics)。

IsTrackingViewState 屬性可讓實作 IStateManager 的型別,與使用此型別之控制項的狀態追蹤協調使用。StateManagedAuthor 類別使用私用 Boolean 欄位 (在 C# 中為 isTrackingViewState 而在 Visual Basic 中為 isTrackingViewStateValue) 存放這個屬性。在 TrackViewState 實作中,StateManagedAuthor 會將 isTrackingViewState 或 isTrackingViewStateValue 設為 true。也會叫用對應於 ViewState 屬性之私用 viewState 或 viewStateValue 欄位的 IStateManager.TrackViewState 方法。TrackViewState 方法會開始追蹤存放在 ViewState 屬性中的項目變更。這表示,如果在 ViewState 上叫用 TrackViewState 之後設定了 ViewState 字典中的項目,該項目會自動標記為已修改。

SaveViewState 方法的實作中,StateManagedAuthor 只會叫用私用 viewState 或 viewStateValue 欄位所對應的方法。同樣的,在 LoadViewState 方法的實作中,StateManagedAuthor 只會叫用 ViewState 屬性所對應的方法。如伺服器控制項屬性範例所述,ViewState 屬性的 StateBag 型別會實作 IStateManager,並且是具有內建狀態管理的字典。

使用在控制項中實作 IStateManager 的型別時,您可以依賴型別維護其本身的狀態,並從控制項的 TrackViewStateSaveViewStateLoadViewState 方法,叫用型別的狀態管理方法。因此,自訂屬性狀態管理範例中描述的 BookNew 控制項會從這些方法的實作中,叫用 StateManagedAuthor 的 TrackViewStateSaveViewStateLoadViewState 方法。

StateManagedAuthorConverter 類別的程式碼清單

下列程式碼所列出的 StateManagedAuthorConverter 是使用 TypeConverterAttribute 屬性,與 StateManagedAuthor 相關聯的型別轉換子 (Type Converter)。StateManagedAuthorConverter 類別可啟用從 String 型別到 StateManagedAuthor 型別的轉換 (反之亦然),如型別轉換子範例所述。在這個範例中使用了 StateManagedAuthorConverter,以便在 BookNew 類別的 Render 方法中列印作者的全名。

注意事項:

StateManagedAuthor 型別不需要型別轉換子即可進行狀態管理。將提供 StateManagedAuthorConverter 做為選擇性公用程式類別,進而提供字串到值的轉換。實作自訂型別的型別轉換子時,應該覆寫型別的 ToString 方法,以便叫用型別轉換子並傳回字串表示,如 StateManagedAuthor 列出的程式碼所示。

' StateManagedAuthorConverter.vb
Imports System
Imports System.ComponentModel
Imports System.ComponentModel.Design.Serialization
Imports System.Globalization
Imports System.Reflection

Namespace Samples.AspNet.VB.Controls
    Public Class StateManagedAuthorConverter
        Inherits ExpandableObjectConverter

        Public Overrides Function CanConvertFrom( _
        ByVal context As ITypeDescriptorContext, _
        ByVal sourceType As Type) As Boolean
            If sourceType Is GetType(String) Then
                Return True
            End If
            Return MyBase.CanConvertFrom(context, sourceType)
        End Function

        Public Overrides Function CanConvertTo( _
        ByVal context As ITypeDescriptorContext, _
        ByVal destinationType As Type) As Boolean
            If destinationType Is GetType(String) Then
                Return True
            End If
            Return MyBase.CanConvertTo(context, destinationType)
        End Function


        Public Overrides Function ConvertFrom( _
            ByVal context As ITypeDescriptorContext, _
            ByVal culture As CultureInfo, ByVal value As Object) _
            As Object
            If value Is Nothing Then
                Return New StateManagedAuthor()
            End If

            If (TypeOf value Is String) Then
                Dim s As String = CStr(value)
                If s.Length = 0 Then
                    Return New StateManagedAuthor()
                End If

                Dim parts() As String = s.Split(" ".ToCharArray)

                If (parts.Length < 2) Or (parts.Length > 3) Then
                    Throw New ArgumentException( _
                        "Name must have 2 or 3 parts.", "value")
                End If

                If parts.Length = 2 Then
                    Return New StateManagedAuthor(parts(0), parts(1))
                End If

                If parts.Length = 3 Then
                    Return New StateManagedAuthor(parts(0), _
                        parts(1), parts(2))
                End If
            End If
            Return MyBase.ConvertFrom(context, culture, value)
        End Function

        Public Overrides Function ConvertTo( _
        ByVal context As ITypeDescriptorContext, _
        ByVal culture As CultureInfo, ByVal value As Object, _
        ByVal destinationType As Type) As Object
            If value IsNot Nothing Then
                If Not (TypeOf value Is StateManagedAuthor) Then
                    Throw New ArgumentException( _
                        "Name must have 2 or 3 parts.", "value")
                End If
            End If

            If destinationType Is GetType(String) Then
                If value Is Nothing Then
                    Return String.Empty
                End If

                Dim auth As StateManagedAuthor = _
                    CType(value, StateManagedAuthor)

                If auth.MiddleName <> String.Empty Then
                    Return String.Format("{0} {1} {2}", _
                    auth.FirstName, _
                    auth.MiddleName, _
                    auth.LastName)
                Else
                    Return String.Format("{0} {1}", _
                        auth.FirstName, _
                        auth.LastName)
                End If
            End If

            Return MyBase.ConvertTo(context, culture, value, _
                destinationType)
        End Function
    End Class
End Namespace
// StateManagedAuthorConverter.cs
using System;
using System.ComponentModel;
using System.ComponentModel.Design.Serialization;
using System.Globalization;
using System.Reflection;

namespace Samples.AspNet.CS.Controls
{
    public class StateManagedAuthorConverter :
        ExpandableObjectConverter
    {
        public override bool CanConvertFrom(
            ITypeDescriptorContext context, Type sourceType)
        {
            if (sourceType == typeof(string))
            {
                return true;
            }
            return base.CanConvertFrom(context, sourceType);
        }

        public override bool CanConvertTo(
            ITypeDescriptorContext context, Type destinationType)
        {
            if (destinationType == typeof(string))
            {
                return true;
            }
            return base.CanConvertTo(context, destinationType);
        }

        public override object ConvertFrom( 
            ITypeDescriptorContext context,
            CultureInfo culture, object value)
        {
            if (value == null)
            {
                return new StateManagedAuthor();
            }

            if (value is string)
            {
                string s = (string)value;
                if (s.Length == 0)
                {
                    return new StateManagedAuthor();
                }

                string[] parts = s.Split(' ');

                if ((parts.Length < 2) || (parts.Length > 3))
                {
                    throw new ArgumentException(
                        "Name must have 2 or 3 parts.", "value");
                }

                if (parts.Length == 2)
                {
                    return new StateManagedAuthor(parts[0], parts[1]);
                }

                if (parts.Length == 3)
                {
                    return new StateManagedAuthor(parts[0], 
                         parts[1], parts[2]);
                }
            }

            return base.ConvertFrom(context, culture, value);
        }

        public override object ConvertTo(ITypeDescriptorContext context,
            CultureInfo culture, object value, Type destinationType)
        {
            if (value != null)
            {
                if (!(value is StateManagedAuthor))
                {
                    throw new ArgumentException(
                        "Name must have 2 or 3 parts.", "value");
                }
            }

            if (destinationType == typeof(string))
            {
                if (value == null)
                {
                    return String.Empty;
                }

                StateManagedAuthor auth = (StateManagedAuthor)value;

                if (auth.MiddleName != String.Empty)
                {
                    return String.Format("{0} {1} {2}",
                        auth.FirstName,
                        auth.MiddleName,
                        auth.LastName);
                }
                else
                {
                    return String.Format("{0} {1}",
                        auth.FirstName,
                        auth.LastName);
                }
            }

            return base.ConvertTo(context, culture,
                value, destinationType);
        }
    }
}

建置及使用範例

自訂屬性狀態管理範例中列出的 BookNew 控制項,以及伺服器控制項屬性範例中列出的 BookType 列舉型別,編譯本主題中所列出的 StateManagedAuthor 和 StateManagedAuthorConverter 類別。

如需編譯及使用自訂控制項範例的詳細資訊,請參閱建置自訂伺服器控制項範例

請參閱

概念

自訂屬性狀態管理範例

伺服器控制項屬性範例

建置自訂伺服器控制項範例

其他資源

開發自訂的 ASP.NET 伺服器控制項