Пример управления состоянием пользовательского типа

Обновлен: Ноябрь 2007

В этом примере показано, как разрешить сложному свойству (свойству, чей тип является классом, в свою очередь имеющим свойства) участие в состоянии представления ASP.NET путем реализации интерфейса IStateManager. Как описано в разделе Пример использования свойств серверного веб-элемента управления, можно использовать свойство ViewState элемента управления для управления состоянием простых свойств. Однако для обеспечения управления состоянием свойства-коллекции или свойства, имеющего подсвойства, необходимо реализовать его как свойство только для чтения, и как часть обычно реализуемого типа свойства IStateManager.

Тип StateManagedAuthor, который задается в этом примере, демонстрирует реализацию IStateManager. Класс StateManagedAuthor является сложным типом, в свою очередь имеющим свойства, например FirstName и LastName. Свойство Author элемента управления BookNew, описанное в разделе Пример управления состоянием настраиваемого свойства, имеет тип StateManagedAuthor.

Реализация интерфейса IStateManager в StateManagedAuthor описывается далее в разделе «Рассмотрение кода».

Код для класса 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 иллюстрирует модель реализации интерфейса IStateManager, обычно используемого сложными типами ASP.NET. Класс StateManagedAuthor задает свойство с именем ViewState, хранящееся в закрытой переменной типа StateBag, которая называется viewState в C# и viewStateValue в Visual Basic. Свойство ViewState имитирует свойство ViewState класса Control. StateManagedAuthor хранит свои свойства в словаре ViewState так же, как элемент управления хранит свои простые свойства в словаре ViewState класса Control.

Интерфейс IStateManager имеет одно свойство IsTrackingViewState и три метода: TrackViewState, SaveViewState и LoadViewState. Эти члены интерфейса IStateManager имеют ту же семантику, что и соответствующие методы класса Control.

Свойство IsTrackingViewState разрешает тип, реализующий IStateManager для соответствия отслеживанию состояния в элементе управления, в котором используется этот тип. Класс StateManagedAuthor использует закрытое поле Boolean (isTrackingViewState в C# и isTrackingViewStateValue в Visual Basic) для хранения этого свойства. В его реализации TrackViewState класс StateManagedAuthor устанавливает isTrackingViewState или isTrackingViewStateValue в значение true. Он также вызывает метод IStateManager.TrackViewState закрытого поля viewState или viewStateValue, соответствующего свойству ViewState. Метод TrackViewState начинает отслеживание изменений элементов, хранящихся в свойстве ViewState. Это означает, что если элемент словаря ViewState устанавливается после вызова метода TrackViewState в ViewState, то этот элемент автоматически помечается как измененный.

В своей реализации метода SaveViewState класс StateManagedAuthor вызывает только соответствующий метод своего закрытого поля viewState или viewStateValue. Аналогично, в своей реализации метода LoadViewState класс StateManagedAuthor вызывает только соответствующий метод своего свойства ViewState. Как описывается в разделе Пример использования свойств серверного веб-элемента управления, тип StateBag свойства ViewState реализует IStateManager и является словарем со встроенным управлением состоянием.

При использовании типа, реализующего IStateManager в элементе управления, поддержка собственного состояния и вызов методов управления состоянием типа из методов TrackViewState, SaveViewState и LoadViewState элемента управления основывается на этом типе. Таким образом, элемент управления BookNew, описанный в разделе Пример управления состоянием настраиваемого свойства, вызывает методы TrackViewState, SaveViewState и LoadViewState класса StateManagedAuthor из его собственной реализации этих методов.

Код для класса StateManagedAuthorConverter

Класс StateManagedAuthorConverter в следующем примере кода является преобразователем типа, связанным с StateManagedAuthor посредством атрибута TypeConverterAttribute. Класс StateManagedAuthorConverter разрешает преобразования от типа String к типу StateManagedAuthor и наоборот, как описано в разделе Пример преобразователя типов. Класс StateManagedAuthorConverter используется в этом примере для печати полного имени автора в методе Render класса BookNew.

ms178652.alert_note(ru-ru,VS.90).gifПримечание.

Типу 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);
        }
    }
}

Построение и использование примера

Скомпилируйте классы StateManagedAuthor и StateManagedAuthorConverter, приведенные в этом разделе, с помощью элемента управления BookNew, приведенного в разделе Пример управления состоянием настраиваемого свойства, и с помощью перечисления BookType, приведенного в разделе Пример использования свойств серверного веб-элемента управления.

Дополнительные сведения о компиляции и использовании примеров пользовательских элементов управления см. в разделе Примеры связывания пользовательского серверного элемента управления.

См. также

Основные понятия

Пример управления состоянием настраиваемого свойства

Пример использования свойств серверного веб-элемента управления

Примеры связывания пользовательского серверного элемента управления

Другие ресурсы

Разработка пользовательских серверных элементов управления ASP.NET