asp.NET自定义服务器控件内部细节系列教程六(复杂属性)

如大家要转载,请保留本人的版权:
/*
*Description:asp.NET自定义服务器控件内部细节系列教程
*Auther:崇崇-天真的好蓝
*MSN:chongchong2008@msn.com
*Dates:2007-05-20
*Copyright:ChongChong2008 YiChang HuBei China
*/
六 复杂属性的声明持久性和类型转换器

   复杂属性的类型不能用简单的字符串表示。这些类型包括非原子值的类型,除字符串外的引用类型以及集合类型。在使用自己的控件时如果遇到复杂属性,那么为了支持声明持久性还需要做额外的工作,并进行状态管理。
   为了使复杂属性具有声明持久性,必须要由以下两个相关的内容一起作用实现:与串行化相关的元数据Attribute和执行给定类型与String类型及其它类型的互相转换的类型转换器,稍后我会讲到。

1.子属性的形式
  复杂属性通常都有子属性,就是通过复杂属性的类型暴露的属性。如:WebControl有一个Font属性,它的类型是System.Web.UI.WebControls.FontInfo,其次有暴露Bold,Name,Size属性。
   1.1 连字符子属性的实现
       为了在控件中持久保存某个子属性,页面开发者必须通过在属性名和子属性名之间用一个连字符来指定子属性。
       如:<asp:TextBox id="textbox1" Font-Name="Verdana" Font-Size="12pt" runat="server"/>

       如果存在与子属性的类型相关的类型转换器,那么页面解析器就可以自动处理带有连字符的子属性语法。为了使设计器可以产生带有连字符的子属性语法,必须对属性及其子属性应用特定的设计期元数据Attrbute,也就是前面提供过的元数据Attrbute。

       下面看看TextBox的Font-Name子属性是如何实现的。

1 public class WebControl : Control
2 {
3    [
4             DesignerSerializationVisibility(DesignerSerializationVisibility.Content) ,
5             NotifyParentProperty(true)
6           ]
7 public FontInfo Font{}
8 }

       DesignerSerializationVisibility.Content告诉设计期的串行化器进入子属性并将子属性的值串行化。设计器在控件的标签中持久保存属性,并对每个子属性产生带连字符的语法。
       NotifyParentProperty(true)使得属性浏览器中对子属性的修改通知一直上传导对象模型,并在被修改了子属性的控件中产生修改通知,使得该控件在设计器中得到“脏”标记。

       同时还必须对复杂属性类型的属性应用NotifyParentProperty(true) attribute.

 1       [TypeConverter(typeof(ExpandableObjectConverter))]
 2 public sealed class FontInfo
 3 {
 4          [NotifyParentProperty(true)]
 5 public string Name{}
 6
 7          [NotifyParentProperty(true)]
 8 public FontUnit Size{}
 9       }
10

       TypeConverterAttribute说明了与FontInfo类型相关的ExpandableObjectConverter类型转换器。ExpandableObjectConverter告诉属性浏览器提供扩展/折叠UI,使得页面开发者可以编辑子属性。

   1.2 内部属性的实现
       在页面的控件标签中嵌套子属性持久保存,如:
       <asp:DataGrid runat="server">
          <HeaderStyle ForeColor="Red"/>
       </asp:DataGrid>

       以上表示HeaderStyle是DataGrid的内部属性,ForeColor是HeaderStyle的子属性。

       为了使内部属性持久性,必须用ParseChindren(true)来标识控件,该attribute告诉页面解析器把控件标签内的内容解析成属性。而且,也必须应用设计期的PersistChildren(false),该attribute告诉设计器把内部内容作为属性而不是子控件持久保存。
       WebControl已经应用了这些attribute,因此当控件派生自WebControl时,就不必重复应用这些attribute.

1       [
2         ParseChindren(true) ,
3         PersistChildren(false)
4       ]
5 public class WebControl : Control,{}

       下面看看这种内部属性具体是如何实现的:

1        [
2             DesignerSerializationVisibility(DesignerSerializationVisibility.Content) ,
3             NotifyParentProperty(true) ,
4             PersistenceMode(PersistenceMode.InnerProperty)
5        ]
6 public virtual TableItemStyle HeaderStyel{}

        PersistenceMode告诉设计器把该属性持久保存为一个内部属性。默认情况下,设计器通过使用带连字符的语法来持久保存控件标签中的子属性。默认的持久保存方式并不要求对某个数性应用PersistenceModeAttribute。

   1.3 内部默认属性的实现
       内部默认属性持久性通常用于持久保存某个控件的集合属性。如:
       <asp:ListBox id="listbox1" Width="100px" runat="server">
           <asp:ListItem>Item 1 </asp:ListItem>
           <asp:ListItem>Item 2 </asp:ListItem>
           <asp:ListItem>Item 3 </asp:ListItem>
       </asp:ListBox>

       当某个控件有内部默认属性的时候,控件标签中的内容就只跟该书性对应。页面解析器不允许控件标签中有任何其他属性。内部默认属性的名称并不是在控件标签内部制定的。
       为了实现内部默认属性的持久性,必须用ParseChildrenAttribute attribute的变种来标记控件,该attribute的第二个参数是内部默认属性的名称。

1       [
2         PersistChildren(true,"默认属性名")
3       ]
4 public class MyControl : WebControl{.}

       而且为了在设计器中正确的持久保存某个内部默认属性,必须用PersistenceMode(PersistenceMode.InnerDefaultProperty)来标记该属性。如:

1       [PersistenceMode(PersistenceMode.InnerDefaultProperty)]
2 public virtual ListItemCollection Items{}

2.属性和类型转换器

  类型转换器是从System.ComponentModel.TypeConverter派生的一个类。页面框架使用类型转换器来实现视图状态的串行化,而且可视化设计器也适用类型转换器在浏览器中显示属性,并实现设计期的串行化。
  在.NET框架中的基本类型都有与其相关的类型转换器,如:Boolean , Char ,Enum,Int32都有与其相关的BooleanConverter,CharConverter,EnumConverter,Int32Converter.
  当某个类型转换器与类型关联时,对该类型的属性它都是自动可用的。

1  [
2    TypeConverterAttribute(typeof(UnitConverter))
3  ]
4 public struct Unit{}

   2.1 实现自己的类型转换器
       需要继承TypeConverter类,重写其Virtual方法

 1 public class myTypeConverter : TypeConverter  
 2 {
 3 public override bool CanConvertFrom(ItypeDescriptorContext context , Type sourceType)
 4 {
 5 if(sourceType == typeof(string))
 6 {
 7 return true;
 8             }
 9
10 return base.CanConvertFrom(context, sourceType);
11          }
12
13 public override bool CanConvertTo(ItypeDescriptorContext context , Type destinationType)
14 {
15 if(destinationType == typeof(string) || destinationType == typeof(InstanceDescriptor))
16 {
17 return true;
18             }
19
20 return base.CanConvertTo(context, destinationType);
21          }
22
23 public override object ConvertFrom(ItypeDescriptorContext context , CultureInfo culture , object value)
24 {
25 //从字符串转换到对象 , 拆分字符串生成对象
26          }
27
28 public override object ConvertTo(ItypeDescriptorContext context , CultureInfo culture , object value , Type destinationType)
29 {
30 //从对象转换到字符串
31          }
32
33        }
34
35

        当某个属性是可读写时,就需要将属性的类型转换成InstanceDescriptor类型,并且解析器也许要产生创建该类型的实例的代码。
        设计器对任何在属性浏览器中显示的复杂属性都需要一个类型转换器,因为它要用类型转换器来在属性浏览器中显示属性的字符串表示。

posted @ 2007-09-06 18:51  玉米疯收  阅读(217)  评论(0编辑  收藏  举报