更新:2007 年 11 月
此示例演示如何通过实现 IStateManager 接口来启用复杂属性(该属性的类型是本身具有属性的类),以参与 ASP.NET 视图状态。如 服务器控件属性示例 中所述,可以使用控件的 ViewState 属性来管理简单属性的状态。但是,要为集合属性或具有子属性的属性提供状态管理,必须将该属性作为只读属性并作为通常实现 IStateManager 的属性类型的一部分来实现。
此示例中定义的 StateManagedAuthor 类型演示如何实现 IStateManager。StateManagedAuthor 类是自身具有子属性(如 FirstName 和 LastName)的复杂类型。自定义属性状态管理示例 中描述的 BookNew 控件的 Author 属性的类型为 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 类阐释 ASP.NET 复杂类型常用来实现 IStateManager 接口的模式。StateManagedAuthor 定义了一个名为 ViewState 且存储在类型 StateBag 的私有变量中的属性,该属性在 C# 中名为 viewState,在 Visual Basic 中名为 viewStateValue。ViewState 属性模仿 Control 类的 ViewState 属性。正如控件在 Control 类的 ViewState 字典中存储其简单属性一样,StateManagedAuthor 在其 ViewState 字典中存储其属性。
IStateManager 接口具有一个 IsTrackingViewState 属性和三个方法:TrackViewState、SaveViewState 和 LoadViewState。IStateManager 接口的成员与 Control 类中的对应方法具有相同的语义。
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 的某类型时,您依赖于该类型来维护其自身状态,并从控件的 TrackViewState、SaveViewState 和 LoadViewState 方法中调用该类型的状态管理方法。因此,自定义属性状态管理示例 中描述的 BookNew 控件将从自身对这些方法的实现中调用 StateManagedAuthor 的 TrackViewState、SaveViewState 和 LoadViewState 方法。
StateManagedAuthorConverter 类的代码清单
下面的代码清单中的 StateManagedAuthorConverter 是通过 TypeConverterAttribute 属性与 StateManagedAuthor 关联的类型转换器。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 类。
有关编译和使用自定义控件示例的更多信息,请参见 生成自定义服务器控件示例。