Custom Type State Management Example

This example shows how to enable a complex property (a property whose type is a class that itself has properties) to participate in ASP.NET view state by implementing the IStateManager interface. As described in Server Control Properties Example, you can use your control's ViewState property to manage the state of simple properties. However, to provide state management for a collection property or a property that has subproperties, you must implement the property as a read-only property, and as part of the property type you typically implement IStateManager.

The StateManagedAuthor type defined in this example demonstrates how to implement IStateManager. The StateManagedAuthor class is a complex type that itself has subproperties such as FirstName and LastName. The Author property of the BookNew control described in Custom Property State Management Example is of type StateManagedAuthor.

The implementation of the IStateManager interface in StateManagedAuthor is described in the "Code Discussion" section later in this topic.

Code Listing for the StateManagedAuthor Class

' StateManagedAuthor.vbOption Strict OnImports System
Imports System.Collections
Imports System.ComponentModel
Imports System.Globalization
Imports System.Web.UI

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

        PublicSubNew()
            Me.New(String.Empty, String.Empty, String.Empty)
        EndSubPublicSubNew(ByVal first AsString, ByVal last AsString)
            Me.New(first, String.Empty, last)
        EndSubPublicSubNew(ByVal first AsString, ByVal middle AsString, _
            ByVal last AsString)
            FirstName = first
            MiddleName = middle
            LastName = last
        EndSub

        < _
        Category("Behavior"), _
        DefaultValue(""), _
        Description("First name of author"), _
        NotifyParentProperty(True) _
        > _
        PublicOverridableProperty FirstName() AsStringGetDim s AsString = CStr(ViewState("FirstName"))
                If s IsNothingThen s = String.Empty
                Return s
            EndGetSet(ByVal value AsString)
                ViewState("FirstName") = value
            EndSetEndProperty

        < _
        Category("Behavior"), _
        DefaultValue(""), _
        Description("Last name of author"), _
        NotifyParentProperty(True) _
        > _
        PublicOverridableProperty LastName() AsStringGetDim s AsString = CStr(ViewState("LastName"))
                If s IsNothingThen s = String.Empty
                Return s
            EndGetSet(ByVal value AsString)
                ViewState("LastName") = value
            EndSetEndProperty

        < _
        Category("Behavior"), _
        DefaultValue(""), _
        Description("Middle name of author"), _
        NotifyParentProperty(True) _
        > _
        PublicOverridableProperty MiddleName() AsStringGetDim s AsString = CStr(ViewState("MiddleName"))
                If s IsNothingThen s = String.Empty
                Return s
            EndGetSet(ByVal value AsString)
                ViewState("MiddleName") = value
            EndSetEndPropertyProtectedOverridableReadOnlyProperty ViewState() _
            As StateBag
            GetIf viewStateValue IsNothingThen
                    viewStateValue = New StateBag(False)

                    If isTrackingViewStateValue ThenCType(viewStateValue, _
                        IStateManager).TrackViewState()
                    EndIfEndIfReturn viewStateValue
            EndGetEndPropertyPublicOverridesFunction ToString() AsStringReturn ToString(CultureInfo.InvariantCulture)
        EndFunctionPublicOverloadsFunction ToString( _
        ByVal culture As CultureInfo) AsStringReturn TypeDescriptor.GetConverter( _
                Me.GetType()).ConvertToString(Nothing, culture, Me)
        EndFunction

#Region "IStateManager implementation"PublicReadOnlyProperty IsTrackingViewState() AsBoolean _
            Implements IStateManager.IsTrackingViewState
            GetReturn isTrackingViewStateValue
            EndGetEndPropertyPublicSub LoadViewState(ByVal savedState AsObject) _
            Implements IStateManager.LoadViewState
            If savedState IsNotNothingThenCType(ViewState, _
                    IStateManager).LoadViewState(savedState)
            EndIfEndSubPublicFunction SaveViewState() AsObject _
            Implements IStateManager.SaveViewState
            Dim savedState AsObject = NothingIf viewStateValue IsNotNothingThen
                savedState = CType(viewStateValue, _
                    IStateManager).SaveViewState
            EndIfReturn savedState
        EndFunctionPublicSub TrackViewState() _
            Implements IStateManager.TrackViewState
            isTrackingViewStateValue = TrueIf viewStateValue IsNotNothingThenCType(viewStateValue, IStateManager).TrackViewState()
            EndIfEndSub
#End Region

        ProtectedSub SetDirty()
            viewStateValue.SetDirty(True)
        EndSubEndClassEndNamespace
// StateManagedAuthor.csusing System;
using System.Collections;
using System.ComponentModel;
using System.Globalization;
using System.Web.UI;

namespace Samples.AspNet.CS.Controls
{
    [
    TypeConverter(typeof(StateManagedAuthorConverter))
    ]
    publicclass StateManagedAuthor : IStateManager
    {
        privatebool _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)
        ]
        publicvirtual 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)
        ]
        publicvirtual 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)
        ]
        publicvirtual String MiddleName
        {
            get
            {
                string s = (string)ViewState["MiddleName"];
                return (s == null) ? String.Empty : s;
            }
            set
            {
                ViewState["MiddleName"] = value;
            }
        }

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

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


        publicoverridestring ToString()
        {
            return ToString(CultureInfo.InvariantCulture);
        }

        publicstring 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

        internalvoid SetDirty()
        {
            _viewState.SetDirty(true);
        }
    }
}

Code Discussion

The StateManagedAuthor class illustrates the pattern for implementing the IStateManager interface commonly used by ASP.NET complex types. StateManagedAuthor defines a property named ViewState stored in a private variable of type StateBag and named viewState in C# and viewStateValue in Visual Basic. The ViewState property mimics the ViewState property of the Control class. StateManagedAuthor stores its properties in its ViewState dictionary just as a control stores its simple properties in the Control class's ViewState dictionary.

The IStateManager interface has one property, IsTrackingViewState, and three methods: TrackViewState, SaveViewState, and LoadViewState. The members of the IStateManager interface have the same semantics as the corresponding methods in the Control class.

The IsTrackingViewState property enables a type that implements IStateManager to coordinate with state tracking in the control in which the type is used. The StateManagedAuthor class uses a private Boolean field (isTrackingViewState in C# and isTrackingViewStateValue in Visual Basic) to store this property. In its implementation of TrackViewState, StateManagedAuthor sets isTrackingViewState or isTrackingViewStateValue to true. It also invokes the IStateManager..::.TrackViewState method of the private viewState or viewStateValue field corresponding to the ViewState property. The TrackViewState method starts the tracking of changes to items stored in the ViewState property. This means that if an item in the ViewState dictionary is set after TrackViewState is invoked on ViewState, the item is automatically marked as modified.

In its implementation of the SaveViewState method, StateManagedAuthor invokes only the corresponding method of its private viewState or viewStateValue field. Similarly, in its implementation of the LoadViewState method, StateManagedAuthor invokes only the corresponding method of its ViewState property. As described in Server Control Properties Example, the StateBag type of the ViewState property implements IStateManager and is a dictionary with state management built into it.

When you use a type that implements IStateManager in your control, you rely on the type to maintain its own state and invoke the type's state management methods from the TrackViewState, SaveViewState, and LoadViewState methods of your control. Thus, the BookNew control described in Custom Property State Management Example invokes the TrackViewState, SaveViewState, and LoadViewState methods of StateManagedAuthor from its own implementation of these methods.

Code Listing For the StateManagedAuthorConverter Class

The StateManagedAuthorConverter in the following code listing is the type converter associated with StateManagedAuthor by means of the TypeConverterAttribute attribute. The StateManagedAuthorConverter class enables conversions from the String type to the StateManagedAuthor type and vice versa as described in the Type Converter Example. The StateManagedAuthorConverter is used in this example to print the author's full name in the Render method of the BookNew class.

Note

The StateManagedAuthor type does not need a type converter for state management. StateManagedAuthorConverter is provided as an optional utility class that provides string-to-value conversions. When you implement a type converter for a custom type you should override the ToString method of your type to invoke the type converter and return a string representation, as shown in the code listing for StateManagedAuthor.

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

Namespace Samples.AspNet.VB.Controls
    PublicClass StateManagedAuthorConverter
        Inherits ExpandableObjectConverter

        PublicOverridesFunction CanConvertFrom( _
        ByVal context As ITypeDescriptorContext, _
        ByVal sourceType As Type) AsBooleanIf sourceType IsGetType(String) ThenReturnTrueEndIfReturnMyBase.CanConvertFrom(context, sourceType)
        EndFunctionPublicOverridesFunction CanConvertTo( _
        ByVal context As ITypeDescriptorContext, _
        ByVal destinationType As Type) AsBooleanIf destinationType IsGetType(String) ThenReturnTrueEndIfReturnMyBase.CanConvertTo(context, destinationType)
        EndFunctionPublicOverridesFunction ConvertFrom( _
            ByVal context As ITypeDescriptorContext, _
            ByVal culture As CultureInfo, ByVal value AsObject) _
            AsObjectIf value IsNothingThenReturnNew StateManagedAuthor()
            EndIfIf (TypeOf value IsString) ThenDim s AsString = CStr(value)
                If s.Length = 0 ThenReturnNew StateManagedAuthor()
                EndIfDim parts() AsString = s.Split(" ".ToCharArray)

                If (parts.Length < 2) Or (parts.Length > 3) ThenThrowNew ArgumentException( _
                        "Name must have 2 or 3 parts.", "value")
                EndIfIf parts.Length = 2 ThenReturnNew StateManagedAuthor(parts(0), parts(1))
                EndIfIf parts.Length = 3 ThenReturnNew StateManagedAuthor(parts(0), _
                        parts(1), parts(2))
                EndIfEndIfReturnMyBase.ConvertFrom(context, culture, value)
        EndFunctionPublicOverridesFunction ConvertTo( _
        ByVal context As ITypeDescriptorContext, _
        ByVal culture As CultureInfo, ByVal value AsObject, _
        ByVal destinationType As Type) AsObjectIf value IsNotNothingThenIfNot (TypeOf value Is StateManagedAuthor) ThenThrowNew ArgumentException( _
                        "Name must have 2 or 3 parts.", "value")
                EndIfEndIfIf destinationType IsGetType(String) ThenIf value IsNothingThenReturnString.Empty
                EndIfDim auth As StateManagedAuthor = _
                    CType(value, StateManagedAuthor)

                If auth.MiddleName <> String.Empty ThenReturnString.Format("{0} {1} {2}", _
                    auth.FirstName, _
                    auth.MiddleName, _
                    auth.LastName)
                ElseReturnString.Format("{0} {1}", _
                        auth.FirstName, _
                        auth.LastName)
                EndIfEndIfReturnMyBase.ConvertTo(context, culture, value, _
                destinationType)
        EndFunctionEndClassEndNamespace
// StateManagedAuthorConverter.csusing System;
using System.ComponentModel;
using System.ComponentModel.Design.Serialization;
using System.Globalization;
using System.Reflection;

namespace Samples.AspNet.CS.Controls
{
    publicclass StateManagedAuthorConverter :
        ExpandableObjectConverter
    {
        publicoverridebool CanConvertFrom(
            ITypeDescriptorContext context, Type sourceType)
        {
            if (sourceType == typeof(string))
            {
                returntrue;
            }
            returnbase.CanConvertFrom(context, sourceType);
        }

        publicoverridebool CanConvertTo(
            ITypeDescriptorContext context, Type destinationType)
        {
            if (destinationType == typeof(string))
            {
                returntrue;
            }
            returnbase.CanConvertTo(context, destinationType);
        }

        publicoverrideobject ConvertFrom( 
            ITypeDescriptorContext context,
            CultureInfo culture, objectvalue)
        {
            if (value == null)
            {
                returnnew StateManagedAuthor();
            }

            if (valueisstring)
            {
                string s = (string)value;
                if (s.Length == 0)
                {
                    returnnew StateManagedAuthor();
                }

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

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

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

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

            returnbase.ConvertFrom(context, culture, value);
        }

        publicoverrideobject ConvertTo(ITypeDescriptorContext context,
            CultureInfo culture, objectvalue, Type destinationType)
        {
            if (value != null)
            {
                if (!(valueis StateManagedAuthor))
                {
                    thrownew 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);
                }
            }

            returnbase.ConvertTo(context, culture,
                value, destinationType);
        }
    }
}

Building and Using the Example

Compile the StateManagedAuthor and StateManagedAuthorConverter classes listed in this topic with the BookNew control listed in Custom Property State Management Example and with the BookType enumeration listed in Server Control Properties Example.

For more information about compiling and using the custom control examples, see Building the Custom Server Control Examples.

See Also

Concepts

Custom Property State Management Example

Server Control Properties Example

Building the Custom Server Control Examples

Other Resources

Developing Custom ASP.NET Server Controls