第十章 复杂属性和状态管理
子属性:复杂属性通常都有子属性(subproperty),就是通过复杂属性的类型暴露的属性。
为使复杂属性具有声明持久性,必须要由以下两个相关内容一起作用实现:
·与串行化相关的元数据 attribute
·执行给定类型与 String 类型及其他类型的互相转换的类型转换器类
在控件标签中持久保存的子属性
为了在控件标签中持久保存某个子属性,页面开发者必须通过在属性名和子属性名之间用一个连字符来指定子属性:
如果存在与子属性的类型相关的类型转换器,那么页面解析器就可以自动处理带有连字符的子属性语法。为了使设计器可以产生带有连字符的子属性语法,必须对属性及其子属性应用特定的设计期元数据 attribute :
2 {
3 [
4 DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
5 NotifyParentProperty(true)
6 ]
7 public FontInfo Font
8 {
}9 .
10 .
11 .
12 }
NotifyParentProperty(true) attribute 使得属性浏览器中对子属性的修改通知一直上传到对象模型,并在被修改了子属性的控件中产生修改通知,使得该控件在设计器中得到“脏”(dirty)标记。这个过程对HTML中的页面设计者在设计器中修改的属性的正确持久保存是很重要的。同时还必须对复杂属性类型的属性应用 NotifyParentProperty(true) attribute 。
内部属性的持久性
内部属性持久性(inner property persistence):由在页面的控件标签中套嵌子属性组成。
2 <HeaderStyle Height="26px"></HeaderStyle>
3 </asp:DataGrid>
ParseChildren(true) attribute:该 attribute 告诉页面解析器把控制标签内的内容解析成属性。
同时,必须应用设计期的 PersistChildren(false) attribute,该 attribute 告诉设计器把内部内容作为属性而不是子控件持久保存(ParseChildrenAttribute 和 PersistChildrenAttribute attribute 中参数的语义使用的是相反的约定)。
WebControl 已经应用了这些 attribute,因此当控件派生自 WebControl 时,就不必重复应用这些 attribute,仅当实现的控件的嵌套内容对应于子控件时,才需要重新应用这个attribute。
2 ParseChildren(true),
3 PersistChildren(false)
4 ]
5 public class WebControl : Control,

6 {
}2 DesignerSerializationVisibility DesignerSerializationVisibility.Content),
3 NotifyParentProperty(true),
4 PersistenceMode(PersistenceMode.InnerProperty)
5 ]
6 public virtual TableItemStyle Height
7 {
}PersistenceMode枚举类型
| 成员名称 | 说明 |
|---|---|
| Attribute | 指定属性或事件保持为特性。 |
| EncodedInnerDefaultProperty | 指定属性作为 ASP.NET 服务器控件的唯一内部文本而进行保持。属性值是 HTML 编码的。只能对字符串做这种指定。 |
| InnerDefaultProperty | 指定属性在 ASP.NET 服务器控件中保持为内部文本。还指示将该属性定义为元素的默认属性。只能指定一个属性为默认属性。 |
| InnerProperty | 指定属性在 ASP.NET 服务器控件中保持为嵌套标记。这通常用于复杂对象;它们具有自己的持久性属性。 |
InnerDefaultProperty:内部默认属性持久性,通常用于持久保存某个控件的集合属性,当某个控件有内部默认属性的时候,控件标签中的内容就只跟该属性对应,页面解析器不允许控件标签中有任何其他属性。
实现内部默认属性持久性:必须用 ParseChildrenAttribute attribute 的变种来标记控件,该 attribute 的第二个参数是内部默认属性的名称:
2 ParseChildren(true,"<DefaultPropertyName>")
3 ]
4 public class MyControl : WebControl
5 {
}2 PersistenceMode(PersistenceMode.InnerDefaultProperty)
3 ]
4 public virtual ListItemCollection Items
5 {
}属性和类型转换器
类型转换器是从 System.ComponentModel.TypeConvert 类(它提供了其他类型和给定类型之间互相转换的实现方法)派生的一个类。
作用:
1)页面框架使用类型转换器来实现视图状态的串行化;
2)可视化设计器使用类型转换器在浏览器中显示属性,并实现设计期的串行化。
把某个类型转换器与类型关联起来:
2 TypeConvert(typeof(UnitConvert))
3 ]
4 public struct Unit
5 {
}实现类型转换器
MapPointConvert:
·重载基类的 CanConvertTo 方法,指定它可以将 MapPoint 类型转换成 String 类型或是 System.ComponentModel.Design.Serialization.InstanceDescriptor 转换器与类类型。当某个属性是可读写时,就需要将属性的类型转换成 InstanceDescriptor 类型,并且解析器也需要产生创建该类型的实例的代码;
·重载基类的 ConvertTo 方法,使得它可以接收某个 MapPoint 实例作为参数,根据需要转换成目的类型,返回一个 String 对象或返回一个 InstanceDescriptor 对象。InstanceDescriptor 对象提供了用来创建以参数形式传递给 ConvertTo 方法的 MapPoint 实例的构造器的有关信息。这些信息是解析器产生创建 MapPoint 类型的一个实例的代码时所使用的。
MapShapeConvert 类派生自 ExpandableObjectConverter,使得可以对属性浏览器中的子属性提供层次 UI。
当某个服务器控件定义了复杂类型的属性时,由于状态管理的关系,服务器控件应该定义成只读的属性。
当某个属性是只读时,页面解析器对该属性的类型并不要求有与之关联的类型转换器。
只有当类型转换器用来产生创建该类型的某个实例的代码时才需要转换成 InstanceDescriptor,而对只读属性并不需要。
需要状态管理的复杂属性都应该定义为只读属性,这样,控件可以完全负责创建并发布代表该属性的类型实例并管理该属性的状态。
状态管理:当重载 TrackViewState、SaveViewState 以及 LoadViewState 方法的时候,基类中这些方法的调用次序是很重要的。
·在重载 TrackViewState 方法中,先将复杂属性的字符串表示保存在 ViewState 字典中,这保证了初始值不会被标记为“脏”,接着调用基类的 TrackViewState 方法;
·在重载 SaveViewState 方法中,将属性当前值同保存在 ViewState 字典中的值进行比较,如果值不同,那么把新的值保存在 ViewState 中,因为在这个阶段执行了属性的跟踪(NotifyParentProperty),所以保存在 ViewState 中的值被自动地标记为“脏”。接着调用基类的 SaveViewState 方法;
·在重载 LoadViewState 方法中,先调用基类的 LoadViewState 方法,以便在回传后恢复 ViewState 字典,然后找到复杂属性的值,用这些值加载复杂属性的状态。
页面如何串行化并恢复视图状态
·在生成阶段,页面利用 System.Web.UI.LosFormatter 类的实例将视图状态串行化成字符串表示,该 System.Web.UI.LosFormatter 类执行的串行化对于许多常用的类型来说都是优化的;
·在 LoadViewState 方法中,页面创建一个 LosFormatter 对象,来并行化代表视图状态的字符串表示,并将它们放回到对象图中。
LosFormatter 用类型转换器来实现值和字符串之间的互相转换,LosFormatter 同样也可以串行化用 Serializable attribute 标记的类型或通过二进制串行化实现了 ISerializable 接口的类型。
LosFormatter 可以优化地串行化两个实用工具类:Pair 和 Triplet 。这两个类都是控件在对对象分组以添加到视图状态时使用的。
重点:
·控件保存在 ViewState 字典中的任何对象或由 SaveViewState 方法返回的任何对象都必须属于由 LosFormatter 类串行化的类型集合,如果控件保存状态所使用的类型并不是由 LosFormatter 来串行化的,那么即使控件通过了编译,也会引起运行期的异常;
·需要选择可以以优化的方式串行化的类型来改进控件以及使用该控件的页面的性能。
IStateManager 接口
页面框架提供了 System.Web.UI.IStateManager 接口,允许其他不是控件的类型也可以参与视图状态管理。
2 {
3 // Methods
4 void LoadViewState(object state);
5 object SaveViewState();
6 void TrackViewState();
7
8 // Properties
9 bool IsTrackingViewState { get; }
10 }
ViewState 属性和默认状态管理
ViewState 属性的类型是 System.Web.UI.StateBag 类,该类是一个字典,它通过实现 IStateManager 接口参与状态管理。
·StateBag 保存了 键/值 对,其中键是字符串,值是对象;
·当某个对象添加到 StateBag 实例中时,StateBag 自动为该对象存储一个附加位,它用来标志存储的对象是否修改过,该附加位是一个布尔标志,说明该属性是否“脏”(dirty)或在初始化后修改过。
StateBag 类对 IStateManager 接口的实现:
·在 StateBag 类的 TrackViewState 方法中,把 IsTrackingViewState 属性使用的私有域设置为 true,这个属性说明修改跟踪是否打开,当 IsTrackingViewState 为 true 时,如果某个 StateItem 添加到 StateBag 中,或某个 StateItem 的值被修改时,StateBag 类把该 StateItem 标记为修改过;
·在 StateBag 类的 SaveViewState 方法中,循环通过其内容,创建并返回两个 ArrayList ,分别代表标记为“脏”的项的键和值;
·在 StateBag 类的 LoadViewState 方法中,执行了在 SaveViewState 中执行的相反操作,用键和值的 ArrayList 把保存的状态加载到相应 StateItem。
Control 类通过委托 ViewState 属性来实现默认的状态管理
2
3 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
4 WebSysDescription("Control_State"),
5 Browsable(false)
6 ]
7 protected virtual StateBag ViewState
8 {
9 get
10 {
11 if (this._viewState == null)
12 {
13 this._viewState = new StateBag(this.ViewStateIgnoresCase);
14 if (this.IsTrackingViewState)
15 {
16 this._viewState.TrackViewState();
17 }
18 }
19 return this._viewState;
20 }
21 }
22
23 protected virtual void TrackViewState()
24 {
25 if (this._viewState != null)
26 {
27 this._viewState.TrackViewState();
28 }
29 this.flags[2] = true;
30 }
31
32 protected virtual object SaveViewState()
33 {
34 if (this.flags[0x20])
35 {
36 this.ViewState["Visible"] = !this.flags[0x10];
37 }
38 if (this._viewState != null)
39 {
40 return this._viewState.SaveViewState();
41 }
42 return null;
43 }
44
45 protected virtual void LoadViewState(object savedState)
46 {
47 if (savedState != null)
48 {
49 this.ViewState.LoadViewState(savedState);
50 object obj1 = this.ViewState["Visible"];
51 if (obj1 != null)
52 {
53 this.flags[0x10] = !((bool) obj1);
54 this.flags[0x20] = true;
55 }
56 }
57 }
在 TrackViewState 开始之前,在ViewState 中添加或修改的项没有标记为“脏”,因此它们并没有保存在与客户端往返传送到串行化视图状态中,这对效率来说是很重要的。
说明:当在 SaveViewState 阶段比较类的当前值与 TrackViewState 阶段保存的初始值时,如果值相等,则不保存,因此该值不会被标记为“脏”,所以提高了效率,反之,则标记为“脏”。
标记为“脏”会执行什么操作?保存标记为“脏”的新项或替换更新项。
1 internal object SaveViewState()
2 {
3 ArrayList list1 = null;
4 ArrayList list2 = null;
5 if (this.bag.Count != 0)
6 {
7 IDictionaryEnumerator enumerator1 = this.bag.GetEnumerator();
8 while (enumerator1.MoveNext())
9 {
10 StateItem item1 = (StateItem) enumerator1.Value;
11 if (item1.IsDirty)
12 {
13 if (list1 == null)
14 {
15 list1 = new ArrayList(5);
16 list2 = new ArrayList(5);
17 }
18 list1.Add(enumerator1.Key);
19 list2.Add(item1.Value);
20 }
21 }
22 if (list1 != null)
23 {
24 return new Pair(list1, list2);
25 }
26 }
27 return null;
28 }
posted on 2006-01-13 20:52 ............. 阅读(219) 评论(0) 收藏 举报
浙公网安备 33010602011771号