★★★【庖丁解牛:纵向切入Asp.net 3.5控件和组件开发技术系列—(4)服务器控件属性】★★★

2020年06月03日 阅读数:49
这篇文章主要向大家介绍★★★【庖丁解牛:纵向切入Asp.net 3.5控件和组件开发技术系列—(4)服务器控件属性】★★★,主要内容包括基础应用、实用技巧、原理机制等方面,希望对大家有所帮助。
 

 

 

4  庖丁解牛系列服务器控件属性

 

 

本章内容 html

4.1  控件属性的做用 git

4.2  简单属性 算法

4.3  属性的设计时特性 express

4.4  复杂属性 编程

4.5  深刻研究—定制本身的属性编辑器 windows

4.6  类型转换器 设计模式

4.7  实现自定义属性数组

[点击下载本书word格式完整目录介绍] 浏览器

4.1  控件属性的做用

 

属性、方法和事件是控件使用者与控件交互的接口。本节主要介绍控件属性。属性分为系统属性和自定义的属性。

 

4-1 Webcontrol类属性窗口

4.1.1  系统属性

当开发控件时若是选择基类,好比选择继承WebControl基类,一旦继承于此类,一些默认的系统属性就会成为当前控件的属性集的一部分,图4-1所示的是WebControl类的系统属性。缓存

能够看到一个通用Web控件所应具有的基本属性都已经有了,在实际开发控件时选择某个基类。

4.1.2  自定义属性

4.1.1节所讲的是系统已有的属性,在开发控件时通常都要为本身的控件增长一些自定义属性。自定义属性与系统属性彻底同样。只是因为不具备系统属性的通用性而须要开发者本身去实现。下面看一下属性的语法格式:

string strText = "默认值";

public string Text

{

    get

    {

        return strText;

    }

 

    set

    {

        strText = value;

    }

}

以上是一个最简单的属性,由一个setget语段组成。注意,setget段不是必需的,好比能够去掉set段表示此属性只容许读取而不容许接收值。

事实上属性的特性范畴还比较多,如简单属性、复杂属性,以及属性在设计时的特性和标记形式的格式等,下面将对这些特性一一进行介绍。

4.2  简单属性

简单属性是类型为字符串的或容易转换为字符串的属性。简单属性在控件的开始标记上自行保留为属性。.NET Framework 类库中的基元值类型,如StringBooleanInt16Int32DateTimeByteCharDoubleEnum均为简单属性。能够经过添加代码将简单属性存储在ViewState字典中,以便在回发间进行状态管理。请看例子:

/// <summary>

/// 得到本书更多内容,请看:

/// http://blog.csdn.net/ChengKing/archive/2008/08/18/2792440.aspx

/// </summary>

public string Value

{

    get

    {

        String s = (String)ViewState["Value"];

        return ((s == null) ? String.Empty : s);

    }

 

    set

    {

        ViewState["Value"] = value;

    }

}

上面声明的简单属性中,属性可接收及其返回值的类型是String,表示本属性为简单属性。另外,简单属性能够直接使用ViewState存储其值,由于简单属性能够直接映射为字符串,而ViewState中能够直接接收的格式也是字符串。

ViewState是页面视图状态机制中的存储机制,是为解决在Web浏览器两次访问之间无状态保持而提供的一种机制,视图信息存储在网页中专用HiddenField区域,并且每次页面提交都会往返于客户端和服务器,所以通常视图主要用于存储少许的文本数据信息,而不适合存储数据量比较大的业务数据。另外,复杂属性的存储也要本身实现视图机制功能,这一点在后面讨论视图机制的章节会详细介绍,这里仅做了解便可。

只要控件中定义了上面的代码段,对应在页面设计器属性窗口中就会包含此项,如图4-2所示。

 

 

4-2  属性窗口中的属性

 

在属性窗口中输入一些文本,打开设计器中的源代码会看到以下标记的ASP.NET代码:

<cc1:controlproperty id="ControlProperty1" runat="server" Value="简单属性">

</cc1:controlproperty>

一样,BooleanInt16Int32DateTimeByteCharDoubleEnum等类型的属性与上面的String类型属性代码标记彻底同样。简单属性比较简单,就讲解到这里。

4.3  属性的设计时特性

.NET Framework为控件设计时属性提供了不少丰富的类,这些属性的功能很是灵活,控制范围普遍,好比能够控制该属性在属性窗口中的显示模式,如:是否在属性窗口中显示该属性,也能够指定此属性必须接收值类型描述,按组分类等,也能够控制文本的标记呈现格式等,甚至能够本身定义一个属性类,实现本身想实现的功能。下面讲一下经常使用的.NET Framework的属性类对控件的支持功能。

Ø  Bindable

指定属性是否能够绑定一个有效数据源,一般使用布尔值进行设置。例如:Bindable(true)。若是使用值true标记属性,表示该属性能够绑定一个有效数据源

Ø  Browsable

指定属性是否应该在属性窗口中显示,使用布尔值设置。通常状况下,对于经常使用的和比较重要的属性设置Browsabletrue,不然设置Browsablefalse

Ø  EditorBrowsable

设置属性在编辑器中的可见性,好比设置在智能提示列表不显示或高级用户才能够看到该属性。

Ø  Category

指定属性在属性浏览器中进行分组显示的类别。该设计时特性帮助可视化编辑器将属性进行逻辑分组。一般分为:外观(Appearance)、行为(Behavior)、布局(Layout)、数据(Data)、操做(Action)、键盘(Key)和鼠标(Mouse)等。若是您安装的是中文版的IDE则默认状况下中文分类和英文分类是通用的即设置成“数据”或“Data”类别是等价的

Ø  Description

设置显示在属性窗口最下面的描述属性功能的文字说明。

Ø  DesignOnly

若是此属性设置为true,表示该属性只能在设计期间使用,不能在页面代码中设置其值。

Ø  ReadOnly

设置该属性是否为只读状态。若是此特性设置为true,则在属性窗口能看到属性,但不能设置其值。另外,经过在属性语句体中把 set 语句段去掉也能够起到相同的效果。

Ø  Themeable

设置该属性是否支持主题特性,默认状况下属性都支持主题。当该属性与界面无关时能够设置其值为false,禁用该属性的主题功能。

Ø  DesignerSerializationVisibility

指定属性是否以及如何在代码中序列化,其值为DesignerSerializationVisibility的枚举值,存在3种设置方式:

—  DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)指定序列化程序不该该序列化属性值;

—  DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)指定应该容许序列化程序序列化属性的值;

—  DesignerSerializationVisibility(DesignerSerializationVisibility.Content)指定序列化程序应该序列化属性的内容,而不是属性自己。此字段为只读。Visible为其默认值。

这里说的序列化是指在IDE中的设计器界面切换到代码视图时,看到的代码标记,或反向切换时把代码标记转化到设计器界面。后面讲复杂属性时会经过示例介绍此属性功能。

Ø  NotifyParentProperty

指示当此设计特性应用到的属性的值被修改时将通知其父属性。换言之,若是属性的父属性应该在该属性值被修改时接到通知,则向该属性应用NotifyParentProperty特性。一般使用布尔值进行设置。通常经常使用于复杂属性通知转换器更新到父级标记

Ø  ParseChildren

使用该设计特性指示当在页面上以声明方式使用控件时,嵌套在服务器控件标记内的XML元素是应该视为属性仍是应视为子控件。一般状况下,包含两种声明方式:

—  ParseChildren(true)表示将子XML元素做为服务器控件的属性分析;

—  ParseChildren(bool childrenasProperty, string defaultProperty),其中childrenasProperty和上面的方式中的布尔值参数意义相同,defaultProperty定义默认状况下将子控件分析为服务器控件的集合属性。

Ø  PersistChildren

该设计特性指示设计时是否应将服务器控件的子控件做为内部嵌套控件保持。若是该特性为PersistChildren(true),则将服务器控件的子控件做为嵌套服务器控件标记保持。若是为PersistChildren(false),则将该控件的属性做为嵌套元素保持。

Ø  PersistenceMode

指定如何将服务器控件属性或事件保持到ASP.NET页面的元数据属性,共存在4种枚举设置方式:

—  PersistenceModePersistenceMode.Attribute)指定属性或事件保持为属性;

—  PersistenceModePersistenceMode.EncodedInnerDefaultProperty)指定属性做为服务器控件的惟一内部文本,若是属性值是HTML编码的,只能对字符串做这种指定;

—  PersistenceModePersistenceMode.InnerDefaultProperty)指定属性在服务器控件中保持为内部文本,还指示将该属性定义为元素的默认属性,只能指定一个属性为默认属性;

—  PersistenceModePersistenceMode.InnerProperty)指定属性在服务器控件中保持为嵌套标记,一般用于复杂对象,它们具备本身的持久性属性。

关于以上4种标记的具体用法下一节会详细介绍

Ø  DefaultValue

指定属性的默认值此特性的设置须要特别谨慎假如设置的值不为空则开发人员在使用时若是本身输入的值与默认值相同则控件不会装载开发人员输入的值也就是说此默认值不能指定为具备有效意义或业务意义的实际值通常设置为空便可。

Ø  DisplayName

指定在属性窗口中显示的别名。此别名仅在属性窗口中看到,当转换器转换到代码视图,以及在页面后面的代码中编码仍是以实际的属性名称为准,而不是以该别名为准。

Ø  ParenthesizedPropertyName

指定属性在属性窗口中显示时是否带有括号至关于在Category分组特性基础上的对属性窗口属性集的排序功能若是不带括号该属性会自动排在该组的前面

Ø  PasswordPropertyText

指定是否设置成密码文本。若是设置为true,则在属性窗口中输入的文本会用特定的密码符号显示,而不是显示原文本;另外,在代码视图中看到的仍为原文本。

Ø  TypeConverter

指定用做此特性所绑定到的对象的转换器的类型。用于转换的类必须从TypeConverter继承。使用ConverterTypeName属性来获取为该特性所绑定到的对象提供数据转换的类名。后面会经过代码示例讲解如何自定义一个本身的类型转换器。

Ø  Editor

指定该属性的编辑器,如系统的文件编辑器、文本编辑器、颜色编辑器,还有集合编辑器等,也能够本身实现编辑器,具体用法后面会讲到。

Ø  ToolBoxItem

此属性为类特性。属于工具箱属性,能够设置当前控件是否在工具箱中显示,以及所在工具箱项的类型名称等信息。默认生成的控件都显示在工具箱中。

Ø  ToolBoxData

此特性为类特性,即不是属性的特性,而是类的特性,设置位置也是在类的上面。ToolBoxData表示从工具箱中拖一个控件到设计界面上时默认显示标记格式,如:

[ToolboxData("<{0}:ControlProperty runat=server></{0}:ControlProperty>")]

能够修改参数字符串,定制为本身想要的格式,但要保证所添加的属性为有意义的属性。

Ø  DefaultProperty

此特性为类特性。它指定服务器控件的默认属性,例如:[DefaultProperty("Text")]

指定用黑色粗体显示默认属性特性的属性名称。通常设置比较重要或经常使用的属性为默认的属性。如TextBox控件的Text属性。

Ø  DefaultEvent

此特性为类特性指定服务器控件的默认事件,例如:[DefaultEvent("OnClicked")]

指定用黑色粗体显示默认事件特性的事件名称。通常设置比较重要或经常使用的属性为默认的事件,如Button控件的OnClick事件。

Ø  ValidationProperty

此特性为类特性,指定该控件的哪一个属性做为验证属性。当该控件与验证控件组合使用时,验证控件会自动验证该特性指定的属性。

Ø  AspNetHostingPermission

此属性为JIT编译时代码访问安全属性。须要使用此属性确保连接到控件的代码具备适当的安全权限。Control类带有两个JIT编译时代码访问安全属性标记:

AspNetHostingPermission(SecurityAction.Demand,Level=AspNetHostingPermissionLevel.Minimal)AspNetHostingPermission(SecurityAction.InheritanceDemand,Level=AspNetHosting PermissionLevel.Minimal).在使用时应把第一个属性应用于当前开发的控件,第二个属性是可选的,由于继承请求是可传递的,在派生类中仍有效。

Ø  ControlBuilder

分析时特性,将自定义控件生成器与控件关联。只有在您但愿使用自定义控件生成器,对页分析器用分析控件的声明性语法的默认逻辑进行修改时,才须要应用此特性。若是仅但愿指定控件标记中的内容是否与属性或子控件对应,请使用ParseChildrenAttribute,而不要使用自定义控件生成器。

Ø  Designer

设计时特性,指定与控件关联的设计器类。控件设计器类用于控制关联的控件在可视化设计器的设计图面上的外观和行为。

还有一些更复杂的,包括在设计模式下的元数据属性类在这里没有列出,由于在后面有专门的章节详细介绍,经过代码示例更容易理解。在这里只要理解上面这些属性类功能,开发通常的控件是没有问题了。

4.4  复杂属性

4.4.1  概述

复杂属性是属性的类型不是简单值类型,或者是一个包含其余属性的类。例如.NET Framework中的StyleFontPoint等都是复杂属性。另外还有集合属性,这里也将它做为复杂属性归类,对于集合属性在本章后面会单独拿出来一节进行详细讲解。

4.4.2  复杂属性的几种标记形式

先看看一个典型的代码段:

<asp:GridView ID="GridView1" runat="server">

    <FooterStyle BackColor="#507CD1" Font-Bold="true" ForeColor="White" />

    <RowStyle BackColor="#EFF3FB" />

    <PagerStyle BackColor="#2461BF" ForeColor="White" HorizontalAlign= "Center" />

    <SelectedRowStyle BackColor="#D1DDF1" Font-Bold="true" ForeColor= "#333333" />

    <HeaderStyle BackColor="#507CD1" Font-Bold="true" ForeColor="White" />

    <EditRowStyle BackColor="#2461BF" />

    <AlternatingRowStyle BackColor="White" />

</asp:GridView>       

<asp:ListBox ID="ListBox1" runat="server">

    <asp:ListItem Value="1"></asp:ListItem>

    <asp:ListItem Value="0"></asp:ListItem>

</asp:ListBox>

代码很是简单,一段是GridView控件的一些属性,另外一段是ListBox控件的一些属性。仔细观察一下这些控件的属性标记,咱们能很容易给它们归类,好比:GridViewID/Runat属性标记类型类似,FootStyle/RowStyle这样的标记相似,还有Font-Bold这样的属性标记,ListBox的集合项ListItem标记也比较特殊等这么多标记类型。咱们在开发控件时固然也但愿可以生成这么多灵活的标记类型,那么本节就详细介绍一下服务端控件的这些标记类型是怎样生成的。

开始以前,有必要说明一下,下面全部代码示例在调试时都是在设计模式下进行的。关于在设计模式下如何调试代码在第2章已经详细讲解过了,若是读者还有疑问请再回顾一下第2章的内容。

一般状况下,复杂属性表现为几种形式:连字符形式属性、内部嵌套形式属性和内部嵌套形式默认属性。下面将介绍以上几种形式复杂属性的具体实现方法。

4.4.2.1  连字符形式的复杂属性标记

连字符复杂属性标记是指属性经过“复杂属性名称-复杂属性的子属性名称”的格式追加到主控件的标记形式。下面用一个例子来说解这种标记。

首先,定义一个复合类Person,结构以下:

/// <summary>

/// 得到本书更多内容,请看:

/// http://blog.csdn.net/ChengKing/archive/2008/08/18/2792440.aspx

/// </summary>

public class Person

{

    private string strName;

    /// <summary>

    /// 姓名

    /// </summary>

    public string Name

    {

        get { return strName; }

        set { strName = value; }

    }

   

    private int intAge;

    /// <summary>

    /// 年龄

    /// </summary>

    public int Age

    {

        get { return intAge; }

        set { intAge = value; }

    }

}

 

u  再在控件中增长一个类型为Person的属性,将如下代码增长到控件中:

  1. /// <summary>
  2. /// 得到本书更多内容,请看:
  3. /// http://blog.csdn.net/ChengKing/archive/2008/08/18/2792440.aspx
  4. /// </summary> 
  5. private Person pPerson;
  6. [Description("复杂属性")]
  7. [Category("复杂属性")]
  8. public Person Person
  9. {
  10.     get
  11.     {
  12.         if (pPerson == null)
  13.         {
  14.             pPerson = new Person();
  15.         }
  16.         return pPerson; 
  17.     }
  18. }

此属性与简单属性的区别有两点:第一,属性接收和返回的类型不是简单类型(intstring 等),而是用咱们本身定义的Person类;第二,复杂属性通常没有set语句,由于通常是对复杂属性的子属性(或子对象)赋值,只要保证它的子属性(子对象)中具备get/set语句便可。编译此控件,在IDE中打开页面,并打开控件的属性窗口,会看到如图4-3所示的界面。

 

图4-3 复杂属性

 

 

另外,在属性窗口中比较这两个属性,您会发现上节讲的简单属性的Value属性能够设置其值,复杂属性Person是只读的,上面咱们没有设置ReadOnly特性。这是由于复杂属性中类型比较复杂,甚至还有嵌套。若是把全部复杂属性包含其子属性的值都放到这一个框中,显然不太方便。这就要求咱们本身根据复杂属性类型增长一些序列化的特性。

解决办法是,为主控件属性Person增长PersistenceModeDesignerSerializationVisibility两个设计特性片断代码以下所示:

  1. /// <summary>
  2. /// 得到本书更多内容,请看:
  3. /// http://blog.csdn.net/ChengKing/archive/2008/08/18/2792440.aspx
  4. /// </summary> 
  5. … …
  6. [PersistenceMode(PersistenceMode.Attribute)]
  7. [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
  8. public Person Person
  9. {
  10.     … …
  11. }

1PersistenceMode特性

PersistenceMode特性指定在页面*.aspx*.ascx文件中如何保持复杂属性,理解此特性用法很是重要,这里详细介绍一下它的用法。PersistenceMode有四种枚举状态:

Ø PersistenceMode.Attribute

表示复杂属性的标记做为主控件的属性,若是复杂属性包含子属性,则子属性持久化成破折号链接的样式,好比:

<asp:GridView ID="GridView1" runat="server">           

    <HeaderStyle BackColor="#507CD1" Font-Bold="true" ForeColor="White" />

</asp:GridView>

上面代码中的Font-Bold对于<HeaderStyle>来讲就是使用了PersistenceMode下的Attribute枚举标记类型。本节例子中就是实现此标记形式。

Ø PersistenceMode.InnerProperty

表示用属性名称做为嵌套标签表示复杂属性,好比GridViewHeaderStyle属性,就是使用了PersistenceMode下的InnerProperty标记形式。代码以下:

<asp:GridView ID="GridView1" runat="server">           

    <HeaderStyle BackColor="#507CD1" Font-Bold="true" ForeColor="White" />

</asp:GridView>

Ø PersistenceMode.InnerDefaultProperty

该特性值与InnerProperty相似都是在主控件外标记复杂属性不一样的是InnerDefaultProperty不须要像InnerProperty那样把属性名做为最外标签通常用于经常使用的或重要复杂属性或集合如:

<asp:ListBox ID="ListBox1" runat="server">

    <asp:ListItem Value="1"></asp:ListItem>

    <asp:ListItem Value="0"></asp:ListItem>

</asp:ListBox>

以上代码中的ListItem它的特色是直接把ListItem单项放到ListBox的标记内部而没有增长一个相似<Items>的标记在ListItem的外面另外InnerDefaultProperty在一个控件类中只能设置一个复杂属性InnerProperty能够设置任意多个复杂属性

通常状况下会把最重要的一个集合属性设置为InnerDefaultProperty枚举标记类型

Ø PersistenceMode.EncodedInnerDefaultProperty

上面的代码中ListItem.Text属性(值为“男”或“女”)除了标记方式与InnerDefaultProperty有点区别外其内容会进行HTML编码好比把HTML标记<div>编码为&lt;div&gt;,即要保证其内容不能再存储HTML标记和子标签。

2DesignerSerializationVisibility特性

此特性表示指定在设计时序列化复杂对象的方式它有三个枚举类型

Ø DesignerSerializationVisibility.Visible

表示代码生成器要对属性自己生成代码。

Ø DesignerSerializationVisibility.Hidden

表示代码生成器不对属性生成代码即在属性窗口设置的值不会被代码生成器生成到*.aspx*.ascx文件中

Ø DesignerSerializationVisibility.Content

表示代码生成器生成复杂属性内容的代码而不是其自己好比在上面的People类中咱们实际要操做的数据是People类下面的 Name/Sex/Age属性即咱们在属性窗口中修改了Name/Sex/Age的值后会仅把这些值经过代码生成器映射到*.aspx*.axcx页面中

果没有设置DesignerSerializationVisibility特性则其值默认为DesignerSerialization Visibility. Visible;通常复杂属性都要设置为DesignerSerializationVisibility.Content

理解了PersistenceMode DesignerSerializationVisibility两个特性的用法咱们再继续完成上面进行中的代码部分为属性Person增长了这两个特性后,再打开Person类定义代码,为该类增长一个类特性TypeConverter,以下所示:

  1. /// <summary>
  2. /// 得到本书更多内容,请看:
  3. /// http://blog.csdn.net/ChengKing/archive/2008/08/18/2792440.aspx
  4. /// </summary> 
  5. [TypeConverter(typeof(ExpandableObjectConverter))]  
  6. public class Person
  7. {
  8.     … …
  9. }

TypeConverter特性指定转换器的类型,ExpandableObjectConverter表示可扩展对象与其余类型的转换器类,该类为系统提供。另外,也能够本身定义转换器规则类,本章后面会有专门介绍。

增长以上属性以后,编译控件再查看属性窗口,就能够在属性窗口中进行设置Person属性的值了,如图4-5所示。

 

 

4-5  设置Person属性值

 

在属性浏览器中为Person设置值,后切换到代码视图,会看到以下标记:

<cc1:controlproperty id="ControlProperty1" runat="server" Person-Age="26" Person-Name="King Zheng"></cc1:controlproperty>

到此咱们就实现以上功能:连字符复杂属性的标记形式。

4.4.2.2  内部嵌套复杂属性标记

连字符复杂属性标记虽然可以实现复杂属性,且代码生成器可以进行正/反向转换,但它把全部复杂属性都挤到主控件的属性上,显示比较臃肿,设想一下,若是GridView把它的<HeadStyle><Rowstyle>等属性都挤到GridView主标记内部,会是什么样子,为了解决这个问题,下面咱们就实现一个相似如下代码中的RowStyle标记形式的复杂属性。

<asp:GridView ID="GridView1" runat="server">

    <RowStyle BackColor="#EFF3FB" />

</asp:GridView>

u  在控件所在项目中增长一个类文件 RowStyle.cs,定义其内容以下:

  1. /// <summary>
  2. /// 得到本书更多内容,请看:
  3. /// http://blog.csdn.net/ChengKing/archive/2008/08/18/2792440.aspx
  4. /// </summary> 
  5. [TypeConverter(typeof(ExpandableObjectConverter))]  
  6. public class RowStyle    //TableItemStyle: Table的Row和Cell样式基类,也能够直
  7.                              //接继承此类
  8. {
  9.     private Color bcBackColor;        
  10.     [NotifyParentProperty(true)]
  11.     public Color BackColor
  12.     {
  13.         get { return bcBackColor; }
  14.         set { bcBackColor = value; }
  15.     }
  16. }

注意不要漏掉TypeConverterNotifyParentProperty,其用途在前面中已经讲过了。

再在主控件中增长一个RowStyle类型的属性,属性名为RowStyle,增长后的属性代码片断以下所示:

 

  1. /// <summary>
  2. /// 得到本书更多内容,请看:
  3. /// http://blog.csdn.net/ChengKing/archive/2008/08/18/2792440.aspx
  4. /// </summary> 
  5. [PersistenceMode(PersistenceMode.InnerProperty)]
  6. [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
  7. [NotifyParentProperty(true)]
  8. [Category("复杂属性")]
  9. [Description("复杂属性——内部嵌套形式")]
  10. public RowStyle RowStyle
  11. {
  12.     get
  13.     {
  14.         if (rsRowStyle == null)
  15.         {
  16.             rsRowStyle = new RowStyle();
  17.         }
  18.         return rsRowStyle; 
  19.     }
  20. }

选择PersistenceMode特性的InnerProperty枚举项,表示生成嵌套标记;至于DesignerSerializationVisibility特性,依然选择Content枚举值,这里也是对复杂属性RowStyle的类对象子属性进行序列化。如还不清楚这两个属性的使用,请到前面的4.1.1节回顾一下。

而后,在主控件加两个类特性,以下所示:

 

  1. /// <summary>
  2. /// 得到本书更多内容,请看:
  3. /// http://blog.csdn.net/ChengKing/archive/2008/08/18/2792440.aspx
  4. /// </summary> 
  5. [ParseChildren(true), PersistChildren(false)] //继承WebControl时能够省略此行
  6. public class ControlProperty : WebControl
  7. {
  8.     … …
  9. }

PerseChildren特性指定页面分析器把控件标记中的内容解析为属性仍是子控件,该属性值设置为true,则表示解析为属性。PersistChildren指定设计器把控件标记中的内容保存为属性仍是子控件,该属性值设置为false,表示保存为属性。

设置了如上几个重要特性后,编译控件,在设计器属性窗口中设置RowStyle属性值,并切换到代码视图,会看到RowStyle的标记形式以下所示:

<cc1:controlproperty id="ControlProperty1" runat="server">

    <RowStyle BackColor="CornflowerBlue" />

</cc1:controlproperty>

只要实现RowStyle复杂类型,那么相似GridView的其余嵌套属性如:<HeaderStyle><FooterStyle><SelectedRowStyle><EditRowStyle>等实现方法用一样方式也能够实现。

在嵌套标记属性比较多的状况下,这些属性看起来效果比上节讲过的连字符复杂属性标记要清晰许多。

另外,还能够按上面所说的步骤对集合类型生成相似的内部嵌套默认属性,如:

<asp:DropDownList id="DropDownList1" runat="server"  >

    <Items>

        <asp:ListItem Value="red">红色</asp:ListItem>

        <asp:ListItem Value="green">绿色</asp:ListItem>

    <Items>

</asp:DropDownList>

基于实现原理与RowStyle相似且本章后面有专门章节详细探讨集合属性,这里不做代码示范集合属性也是很是重要和经常使用的复杂属性类型

4.4.2.3  内部嵌套默认复杂属性标记

内部嵌套默认属性与内部嵌套属性很是相似,通常用于设置某个控件的集合属性。好比标准服务器控件中的DropDownList控件中的属性均为内部嵌套默认属性,代码以下:

<asp:DropDownList id="DropDownList1" runat="server" >

    <asp:ListItem Value="red">红色</asp:ListItem>

    <asp:ListItem Value="green">绿色</asp:ListItem>

</asp:DropDownList>

内部嵌套默认属性的ListItem标记外部没有像内部集合属性同样嵌套在<Items></Items>中,而后把<Items>嵌套在主控件标记中,而是直接把<asp:listItem></asp:listItem>嵌套在主控件标记内部,通常当该控件只有一个集合复杂属性的状况时使用;而当一个集合中有多个集合或复杂属性时通常设置为内部嵌套复杂属性标记形式。

为主控件增长集合属性以前,先要创建两个类:

Ø ListItem类:集合中的单项定义类。

Ø Items类:集合类,提供ListItem的容器以及一些经常使用的添加/删除等子项方法。

1ListItem类完整代码

 

  1. /// <summary>
  2. /// 得到本书更多内容,请看:
  3. /// http://blog.csdn.net/ChengKing/archive/2008/08/18/2792440.aspx
  4. /// </summary> 
  5. [ToolboxItem(false)]
  6. [TypeConverter(typeof(ExpandableObjectConverter))]    
  7. public class ListItem : Control
  8. {        
  9.     private string _Text;
  10.     private string _Value;        
  11.     public ListItem()
  12.     { }
  13.     public ListItem(string strText,string strValue)
  14.     {            
  15.         this._Text = strText;
  16.         this._Value = strValue;            
  17.     }        
  18.     /// <summary>
  19.     /// 文本属性
  20.     /// </summary>     
  21.     [NotifyParentProperty(true)]           
  22.     public string Text
  23.     {
  24.         get { return _Text; }
  25.         set { _Text = value; }
  26.     }
  27.     /// <summary>
  28.     /// 值属性
  29.     /// </summary>     
  30.     [NotifyParentProperty(true)]
  31.     public string Value
  32.     {
  33.         get { return _Value; }
  34.         set { _Value = value; }
  35.     }
  36. }

此子项类的代码比较简单惟一要说明的是上面的[ToolBoxItem(false)]表示不在IDE工具箱的控件集合中显示很显然这不是一个控件不能在工具箱集合列表中显示通常除了主控件以外的其他类都要把ToolBoxItem类元数据特性置为false不然当使用者拖一个不完整的控件标记到页面上时可能出现控件不能使用的状况

2Items类的完整代码

 

  1. /// <summary>
  2. /// 得到本书更多内容,请看:
  3. /// http://blog.csdn.net/ChengKing/archive/2008/08/18/2792440.aspx
  4. /// </summary> 
  5. /// <summary>
  6. /// 菜单实现类[实用泛型集合]    
  7. /// </summary>
  8. [
  9. ToolboxItem(false),
  10. ParseChildren(true)
  11. ]
  12. public class Items : List<ListItem>
  13. {
  14.     #region 定义构造函数
  15.     public Items()
  16.         : base()
  17.     {
  18.     }
  19.     #endregion
  20.     /// <summary>
  21.     /// 获得集合元素的个数
  22.     /// </summary>
  23.     public new int Count
  24.     {
  25.         get
  26.         {
  27.             return base.Count;
  28.         }
  29.     }
  30.     /// <summary>
  31.     /// 表示集合是否为只读
  32.     /// </summary>
  33.     public bool IsReadOnly
  34.     {
  35.         get
  36.         {
  37.             return false;
  38.         }
  39.     }
  40.     /// <summary>
  41.     /// 添加对象到集合
  42.     /// </summary>
  43.     /// <param name="item"></param>
  44.     public new void Add(ListItem item)
  45.     {
  46.         base.Add(item);
  47.     }
  48.     /// <summary>
  49.     /// 清空集合
  50.     /// </summary>
  51.     public new void Clear()
  52.     {
  53.         base.Clear();
  54.     }
  55.     /// <summary>
  56.     /// 判断集合中是否包含元素
  57.     /// </summary>
  58.     /// <param name="item"></param>
  59.     /// <returns></returns>
  60.     public new bool Contains(ListItem item)
  61.     {
  62.         return base.Contains(item);
  63.     }
  64.     /// <summary>
  65.     /// 移除一个对象
  66.     /// </summary>
  67.     /// <param name="item"></param>
  68.     /// <returns></returns>
  69.     public new bool Remove(ListItem item)
  70.     {
  71.         return base.Remove(item);
  72.     }
  73.     /// <summary>
  74.     /// 设置或取得集合索引项
  75.     /// </summary>
  76.     /// <param name="index"></param>
  77.     /// <returns></returns>
  78.     public new ListItem this[int index]
  79.     {
  80.         get
  81.         {
  82.             return base[index];
  83.         }
  84.         set
  85.         {
  86.             base[index] = value;
  87.         }
  88.     }          
  89. }

这里的Items采用泛型集合,继承list<T>强类型集合做为基类,此外在System.Collections. Generic命名空间中还有其余一些强类型集合。

增长完上面两个类后,实现内部默认集合属性,还须要设置两个类设计特性:一是在控件类前设置ParseChildren(true,“默认属性名称”),指定主控件中的属性名称表示是属性,而不是子控件,ParseChildren4.3节已经作了讲解;二是设置[PersistChildren(false)]类特性表示要把集合标记做为属性方式保持和进行序列化

在主控件的集合属性前要设置以下三个特性:

[PersistenceMode(PersistenceMode.InnerDefaultProperty)]

[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]

[NotifyParentProperty(true)]

第一个特性,指定集合属性为内部默认属性;第二个特性,指定要序列化的是集合属性的内容,而不是集合属性自己;第三个特性,指定集合属性的子属性修改时会通知父属性。

新建一个Web自定义控件文件,并按以上所述进行设置,控件主类核心代码以下:

 

  1. /// <summary>
  2. /// 得到本书更多内容,请看:
  3. /// http://blog.csdn.net/ChengKing/archive/2008/08/18/2792440.aspx
  4. /// </summary> 
  5. /// <summary>
  6. /// 控件主类[复杂属性-内部默认属性]
  7. /// </summary>
  8. [DefaultProperty("Text")]
  9. [ToolboxData("<{0}:CollectionControlProperty runat=server></{0}:Collection ControlProperty>")]
  10. [PersistChildren(false)]
  11. [ParseChildren(true"Items")] 
  12. public class CollectionControlProperty : WebControl
  13. {
  14.     private Items items;
  15.     [PersistenceMode(PersistenceMode.InnerDefaultProperty)]
  16.     [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
  17.     [NotifyParentProperty(true)]
  18.     [TypeConverter(typeof(CollectionConverter))]
  19.     [Category("复杂属性")]
  20.     [Description("复杂属性——内部默认嵌套形式")]
  21.     public Items Items
  22.     {
  23.         get
  24.         {
  25.             if (this.items == null)
  26.             {
  27.                 this.items = new Items();
  28.             }
  29.             return this.items;
  30.         }
  31.     }
  32.     … …
  33. }

除了设置上面所提到的属性外,本集合类还多了一个特性[TypeConverter(typeof(collection Converter)]。此特性指定本集合属性转换到代码视图时采用系统默认的集合转换器。针对经常使用的类型,系统提供了一组默认的转换器,后面章节会介绍怎样建立自定义复杂类型的类型转换器。

通过以上设置后,在页面上拖动一个控件,并在属性窗口中增长填加几个子项,如图4-6所示。

 

4-6  集合编辑器

 

设置完后,回到源代码视图,会看到刚才设置好的几个子项:

<cc1:CollectionControlProperty ID="CollectionControlProperty1" runat="server">

    <cc1:ListItem ID="ListItem1" runat="server" Text="红色" Value="red">

    </cc1:ListItem>

    <cc1:ListItem ID="ListItem2" runat="server" Text="蓝色" Value="blue">

    </cc1:ListItem>

    <cc1:ListItem ID="ListItem3" runat="server" Text="绿色" Value="green">

    </cc1:ListItem>

</cc1:CollectionControlProperty>

本节主要是完成一个复杂集合属性,并把集合属性设置为默认属性。本节示例控件的全部源代码请参阅随书光盘中的内容。

4.4.2.4  内部嵌套编码默认属性

请看下面这段咱们常用的代码:

<asp:DropDownList id="DropDownList1" runat="server" >

    <asp:ListItem Value="red">红色</asp:ListItem>

    <asp:ListItem Value="green">绿色</asp:ListItem>

</asp:DropDownList>

细心的读者可能看到,表示Text(“红色”位置的属性)的属性不像Value属性是附属于ListItem标记,而是在两个<asp:ListItem></asp:ListItem>标记之间呈现。这样的标记主要用于显示非HTML标记或非子控件的纯文本,本节主要完成这种格式属性的实现。

为了保留前面控件已有的功能,从新定义两个类Item2ListItem2

1Items集合类

代码以下:

  1. /// <summary>
  2. /// 得到本书更多内容,请看:
  3. /// http://blog.csdn.net/ChengKing/archive/2008/08/18/2792440.aspx
  4. /// </summary> 
  5. /// <summary>
  6. /// 菜单实现类[实用泛型集合]
  7. /// </summary>
  8. [
  9. ToolboxItem(false),
  10. ParseChildren(true)
  11. ]
  12. public class Items2 : List<ListItem2>
  13. {
  14.     //省略,此集合类内部代码与Items彻底相同
  15. }
2
ListItem2子项类

代码以下:

 

 

  1. /// <summary>
  2. /// 得到本书更多内容,请看:
  3. /// http://blog.csdn.net/ChengKing/archive/2008/08/18/2792440.aspx
  4. /// </summary> 
  5. /// <summary>
  6. /// 子项类
  7. /// </summary>
  8. [ToolboxItem(false)]
  9. [TypeConverter(typeof(ExpandableObjectConverter))]    
  10. [ParseChildren(true"Text")]  
  11. [PersistChildren(false)]       
  12. public class ListItem2 : Control  
  13. {        
  14.     private string _Text;
  15.     private string _Value;        
  16.     public ListItem2()
  17.     { }
  18.     public ListItem2(string strText, string strValue)
  19.     {            
  20.         this._Text = strText;
  21.         this._Value = strValue;            
  22.     }        
  23.     /// <summary>
  24.     /// 文本属性
  25.     /// </summary>     
  26.     [NotifyParentProperty(true)]
  27.     [PersistenceMode(PersistenceMode.EncodedInnerDefaultProperty)]
  28.     [Description("复杂属性——内部默认嵌套形式")]
  29.     public string Text
  30.     {
  31.         get { return _Text; }
  32.         set { _Text = value; }
  33.     }
  34.     /// <summary>
  35.     /// 值属性
  36.     /// </summary>     
  37.     [NotifyParentProperty(true)]
  38.     public string Value
  39.     {
  40.         get { return _Value; }
  41.         set { _Value = value; }
  42.     }
  43. }

此子项类要作一些特性设置,类元特性须要增长两个特殊的特性:

Ø [ParseChildren(true, "Text")] 

Ø [PersistChildren(false)]       

第一个特性表示将子Text元素做为服务器控件的属性分析;第二个特性表示将该控件的属性做为嵌套元素保持。

另外,还要注意要对做为编码内部属性的属性进行设置,好比这里为Text属性加上:

[PersistenceMode(PersistenceMode.EncodedInnerDefaultProperty)] 

u  进行如上设置后,增长一个主控件文件,并进行以下所示设置:

 

  1. /// <summary>
  2. /// 得到本书更多内容,请看:
  3. /// http://blog.csdn.net/ChengKing/archive/2008/08/18/2792440.aspx
  4. /// </summary> 
  5. [DefaultProperty("Text")]
  6. [ToolboxData("<{0}:EncodedInnerDefaultPropertyControl runat=server></{0}: EncodedInnerDefaultPropertyControl>")]
  7. [PersistChildren(false)]
  8. [ParseChildren(true"Items")]
  9. public class EncodedInnerDefaultPropertyControl : WebControl
  10. {
  11.     public EncodedInnerDefaultPropertyControl()
  12.     { 
  13.     }
  14.     private Items2 items;
  15.     [PersistenceMode(PersistenceMode.InnerDefaultProperty)]
  16.     [DesignerSerializationVisibility(DesignerSerializationVisibility. Content)]
  17.     [NotifyParentProperty(true)]
  18.     [TypeConverter(typeof(CollectionConverter))]
  19.     [Category("复杂属性")]
  20.     [Description("复杂属性——内部默认嵌套形式")]
  21.     public Items2 Items
  22.     {
  23.         get
  24.         {
  25.             if (this.items == null)
  26.             {
  27.                 this.items = new Items2();
  28.             }
  29.             return this.items;
  30.         }
  31.     }
  32.     … …
  33. }

上面主控件类与 4.4.2.3中主控件类设置彻底相同,这里就再也不做说明。

设置完成后编译控件库,拖动此控件到页面中,能够看到在属性窗口增长了几个集合项,如图4-7所示。

 

4-7  属性窗口

集合设置界面与4.4.2.3节中的彻底相同,但切换到代码视图界面,会发现序列化后的代码变化了,以下所示:

<cc1:EncodedInnerDefaultPropertyControl ID="EncodedInnerDefaultProperty Control1" runat="server" Items-Capacity="4">    

    <cc2:ListItem2 ID="ListItem22" runat="server" Value="red">红色</cc2:ListItem2>

    <cc2:ListItem2 ID="ListItem23" runat="server" Value="blue">蓝色</cc2:ListItem2>

</cc1:EncodedInnerDefaultPropertyControl>

以看到Text属性已经再也不做为ListItem的直接属性,而是嵌套在<ListItem2> </ListItem2>之间。

本节主要说明控件内部嵌套编码默认属性格式的实现。在实现时须要注意的是,对ListItem2子项类进行一些元数据特性设置,由于Text是属于ListItem2类的属性。

4.4.3  深刻研究—复杂属性分析器

4.4.3.1  使用AddParsedSubObject控制复杂内容(子控件)

4.4.2节已经把各类各样的复杂属性类型都实现了,这些都是在实际开发中经常使用的属性格式,可以知足绝大多数开发须要。

这一节讲解稍微复杂一点的属性格式。通常在一个控件中只能设置单个的属性为内部默认属性,好比4.4.2.3节中实现的属性:

<cc1:CollectionControlProperty ID="CollectionControlProperty1" runat="server">

    <cc1:ListItem ID="ListItem1" runat="server" Text="红色" Value="red">

    </cc1:ListItem>

    <cc1:ListItem ID="ListItem2" runat="server" Text="蓝色" Value="blue">

    </cc1:ListItem>

    <cc1:ListItem ID="ListItem3" runat="server" Text="绿色" Value="green">

    </cc1:ListItem>      

</cc1:CollectionControlProperty>  

其中Items属性设置成了内部默认属性,若是控件中须要多个内部默认属性的格式,默认分析器对此是不支持的。若是强行设置了两个默认属性格式的属性,控件能够编译经过,但在页面的属性窗口设置多个复杂属性后,进行代码与设计器视图切换时系统会报如下错误:

 

 

这说明不能利用默认的解析器分析多个设置了默认属性格式的子标记。为了解决这个问题,其中一种方法能够重写AddParsedSubObject来定制本身的页面解析子控件方法。主控件核心源代码以下:

 

  1. /// <summary>
  2. /// 得到本书更多内容,请看:
  3. /// http://blog.csdn.net/ChengKing/archive/2008/08/18/2792440.aspx
  4. /// </summary> 
  5. /// <summary>
  6. /// 本控件包含三个集合复杂属性: 两个内部默认嵌套形式; 一个内部嵌套形式.
  7. /// </summary>
  8. [ToolboxData("<{0}:MultiCollectionControlProperty runat=server></{0}:Multi CollectionControlProperty>")]
  9. [ParseChildren(false)]     
  10. public class MultiCollectionControlProperty : WebControl
  11. {
  12.     private Items items;
  13.     [PersistenceMode(PersistenceMode.InnerDefaultProperty)]
  14.     [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
  15.     [NotifyParentProperty(true)]
  16.     [TypeConverter(typeof(CollectionConverter))]
  17.     [Category("复杂属性")]
  18.     [Description("复杂属性——内部默认嵌套形式")]
  19.     public Items Items
  20.     {
  21.         get
  22.         {
  23.             if (this.items == null)
  24.             {
  25.                 this.items = new Items();
  26.             }
  27.             return this.items;
  28.         }
  29.     }
  30.     private Items2 items2;
  31.     [PersistenceMode(PersistenceMode.InnerDefaultProperty)]
  32.     [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
  33.     [NotifyParentProperty(true)]
  34.     [TypeConverter(typeof(CollectionConverter))]
  35.     [Category("复杂属性")]
  36.     [Description("复杂属性——内部默认嵌套形式")]
  37.     public Items2 Items2
  38.     {
  39.         get
  40.         {
  41.             if (this.items2 == null)
  42.             {
  43.                 this.items2 = new Items2();
  44.             }
  45.             return this.items2;
  46.         }
  47.     }
  48.     private Items3 items3;
  49.     [PersistenceMode(PersistenceMode.EncodedInnerDefaultProperty)]
  50.     [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
  51.     [NotifyParentProperty(true)]
  52.     [TypeConverter(typeof(CollectionConverter))]
  53.     [Category("复杂属性")]
  54.     [Description("复杂属性——内部编码嵌套形式")]
  55.     public Items3 Items3
  56.     {
  57.         get
  58.         {
  59.             if (this.items3 == null)
  60.             {
  61.                 this.items3 = new Items3();
  62.             }
  63.             return this.items3;
  64.         }
  65.     }
  66.     protected override void AddParsedSubObject(object obj)
  67.     {            
  68.         if (obj is ListItem)
  69.         {
  70.             if (this.items == null)
  71.             {
  72.                 this.items = new Items();
  73.             }
  74.             this.items.Add((ListItem)obj);
  75.         }
  76.         if (obj is ListItem2)
  77.         {
  78.             if (this.items2 == null)
  79.             {
  80.                 this.items2 = new Items2();
  81.             }
  82.             this.items2.Add((ListItem2)obj);
  83.         }
  84.         if (obj is ListItem3)
  85.         {
  86.             if (this.items3 == null)
  87.             {
  88.                 this.items3 = new Items3();
  89.             }
  90.             this.items3.Add((ListItem3)obj);
  91.         }
  92.     }
  93. }

本主控件类包含三个集合复杂属性两个内部默认嵌套形式属性(ItemsItems2);一个内部编码嵌套形式属性(Items3)。这三个集合类的子项也是使用前面示例中的子项类,其中ListItem3代码省略它与前面的ListItem1类的内部代码彻底同样只是类名不一样

主类MultiCollectionControlProperty前面要加个很重要的元特性 [ParseChildren(false)],指定页解析器把嵌套的内容做为子控件解析。

另外,还能够重写AddParseSubObject,定制自定义的页面解析实现,页面在设计模式下解析(从代码视图切换到设计视图)时,每检测到一个内部子控件都会触发此方法,此方法的参数object就是当前内部子控件标记生成的对象。方法体中的三个if语句分别判断当前对象是什么类型,若是是ListItem类型就把它添加到相关的类集合中,如上面代码把ListItem类型的对象增长到了Items集合中。只有这样,咱们在设计视图中查看属性窗口中值时,当前集合才有值显示在属性集合编辑器中(弹出窗口编辑器)。在增长一个子项到集合中时,还要注意第一次往集合中增长子项时,集合值为null,要先为当前集合生成对象实例。

事实上控件中的嵌套标记,不只能够内置集合类型子标记,还能够增长任意类型的标记,只要子标记具备前缀标志和runat属性便可;若是没有前缀和runat属性,系统也不会报错,只是页面解析器会把不具备前缀和runat属性的整个块标记都用LiteralControl包装后返回LiteralControl的对象(返回给AddParseSubObject的参数obj),而无论此块有多大。

编译此控件后拖一个控件到页面中会在属性窗口中看到三个并列的集合属性如图4-8所示。

 

4-8  属性窗口

 

为三个集合属性分别设置几个子项切换到源代码视图会看到以下源代码:

<cc1:MultiCollectionControlProperty ID="MultiCollectionControlProperty1" runat="server">

    <cc2:ListItem runat="server" Text="红色" Value="red" ID="ListItem1"> </cc2:ListItem>

    <cc2:ListItem runat="server" Text="绿色" Value="green" ID="ListItem2"> </cc2:ListItem>

    <cc2:ListItem2 runat="server" Value="blue" ID="ListItem21">蓝色</cc2:ListItem2>

    <cc2:ListItem2 runat="server" Value="gray" ID="ListItem22">灰色</cc2:ListItem2>

    <cc2:ListItem3 runat="server" Text="黄色" Value="yellow" ID="ListItem31"> </cc2:ListItem3>

    <cc2:ListItem3 runat="server" Text="淡蓝" Value="lightblue" ID="ListItem32"> </cc2:ListItem3>

</cc1:MultiCollectionControlProperty>

能够看到ListItemListItem2ListItem3很是有序地嵌套在主控件内部从而实现了主控件内部多个复杂默认属性嵌套功能

AddParseSubObject方法当然可以帮助咱们实现控件内部多个复杂默认属性的嵌套功能但它也有局限性就是前面提到过的子标记必须是子控件形式标记子标记要具备前缀标志和runat属性,不然整个非子控件类型块标记都用LiteralControl包装后返回LiteralControl的对象(返回给AddParseSubObject的参数obj而无论此块有多大

以上是经过重写AddParseSubObject方法实现页面解析功能;另外,.NET Framework为控件设计模式支持专门提供了一个控件构造类:System.Web.UI.ControlBuilder,经过继承此类也能够实现定制页面解析,并且更灵活,后面会专门对比进行介绍。

4.4.3.2  使用ControlBuilder解析复杂内容

经过System.Web.UI.ControlBuilder类定制页面解析逻辑,能够定制任意类型的标记,而不像重写AddParseSubObject方法那样限定子标记必须是子控件,且必须有前缀和runat属性,下面直接经过一个例子来讲明一下此类的用法。

首先创建两个文件ScriptItem.csScriptItemCollection.cs,分别定义ScriptItem类和ScriptItemCollection类。其中,ScriptItem类主要存储用户自定义的客户端脚本命令(JavaScript块),ScriptItemCollection能够定义一个集合容器,每一个项都是一个 ScriptItem项。与前面讲的集合实现很是相似。这两个类的完整代码以下:

1ScriptItem

 

  1. /// <summary>
  2. /// 得到本书更多内容,请看:
  3. /// http://blog.csdn.net/ChengKing/archive/2008/08/18/2792440.aspx
  4. /// </summary> 
  5. private string _Text;        
  6. [DefaultValue("")]
  7. [Editor("System.ComponentModel.Design.MultilineStringEditor,System.Design"typeof(UITypeEditor))]
  8. [PersistenceMode(PersistenceMode.EncodedInnerDefaultProperty)]       
  9. [NotifyParentProperty(true)]
  10. /// <summary>
  11. /// JavaScript脚本块
  12. /// </summary>        
  13. public string Text
  14. {
  15.     get
  16.     {
  17.         return _Text;
  18.     }
  19.     set
  20.     {
  21.         _Text = value;
  22.     }
  23. }

该类中的Text就是用于存储用户定义的脚本块;Editor元数据特性指定在属性窗口中Text属性的编辑器是一个下拉块输入编辑器,关于属性编辑器下一节会详细讲解,这里仅知道它的功能便可。

须要注意的是,在上一节使用AddParsedSubObject实现页面解析子控件时,要嵌套的三个集合的子标记:ListItemListItem2ListItem3都继承了Control基类,目的是把这些子标记做为子控件(也就具备了前缀和runat属性),而这里的ScriptItem没有继承任何基类,这样就避免了继承一些基类中的冗余属性和方法。

2ScriptItemCollecton

/// <summary>

/// 得到本书更多内容,请看:

/// http://blog.csdn.net/ChengKing/archive/2008/08/18/2792440.aspx

/// </summary>

 

[ToolboxItem(false)]   

public class ScriptItemCollection : List<ScriptItem>

{

    public ScriptItemCollection() : base() { }

    public ScriptItemCollection(int capacity) : base(capacity) { }

    public ScriptItemCollection(IEnumerable<ScriptItem> collection):base (collection) { }

}

 

u  定义这两个类以后,实现咱们本身的ControlBuilder类,能够直接继承该类并实现本身的方法,已经预先实现好了的构造器类代码以下所示:

 

  1. /// <summary>
  2. /// 得到本书更多内容,请看:
  3. /// http://blog.csdn.net/ChengKing/archive/2008/08/18/2792440.aspx
  4. /// </summary> 
  5. public class ScriptItemBuilder : ControlBuilder
  6. {       
  7.     public override Type GetChildControlType(string tagName, IDictionary attributes)
  8.     {
  9.         if (string.Compare(tagName.ToLower(), "scriptitem"false, CultureInfo. InvariantCulture) == 0)
  10.         {
  11.             return typeof(ScriptItem);
  12.         }
  13.         return null;
  14.     }
  15.     public override bool AllowWhitespaceLiterals()
  16.     {
  17.         return false;            
  18.     }        
  19. }

在该类中要作的最重要的事情是重写方法GetChildControlType,在页面解析器分析主控件的每一个子标记时,都会调用一次此方法。

该方法的第一个参数表示当前正在解析的控件标记字符串,第二个参数表示标记上全部特性的字典集合。方法体中的if语句的功能是,假如当前解析的标记是“scriptitem”(就是后面定义到主控件的集合属性名称),则返回ScriptItem类的类型,且经过ToLower()方法实现不区分大小写。须要注意的是,这里咱们作的工做很是简单,只是匹配相应的字符串标记并返回一个类型。而AddParsedSubObject则要本身处理当前对象的值。还有个重写方法AllowWhitespaceLiterals用于指定控件的开始标记和结束标记之间是否容许存在空白。

定义完本身的构造器后,经过为主控件增长以下元数据特性,指定主控件的解析器:

[ControlBuilder(typeof(ScriptItemBuilder))]

u  设置完后,完整的主控件类源代码以下:

 

  1. /// <summary>
  2. /// 得到本书更多内容,请看:
  3. /// http://blog.csdn.net/ChengKing/archive/2008/08/18/2792440.aspx
  4. /// </summary> 
  5. [ToolboxData("<{0}:ControlBuilderControl runat=server></{0}:ControlBuilder Control>")]
  6. [ParseChildren(true"ScriptItems")]
  7. [ControlBuilder(typeof(ScriptItemBuilder))]
  8. public class ControlBuilderControl : WebControl
  9. {
  10.     private ScriptItemCollection _ScriptItems = new ScriptItemCollection();
  11.     /// <summary>
  12.     /// 脚本命令集合属性
  13.     /// </summary>
  14.     [PersistenceMode(PersistenceMode.InnerProperty)]
  15.     [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
  16.     [Description("工具按钮集设置")]
  17.     [Category("工具按钮——属性设置")]
  18.     public ScriptItemCollection ScriptItems
  19.     {
  20.         get
  21.         {
  22.             if (_ScriptItems == null)
  23.             {
  24.                 _ScriptItems = new ScriptItemCollection();
  25.             }
  26.             return _ScriptItems;
  27.         }
  28.     }
  29.     //… …  
  30. }

整个主控件只包括一个集合属性。须要注意的是咱们把这个属性定义成内部嵌套标记形式,即咱们在<ScriptItem>标记外面又嵌套了一个<ScriptItems>,把ScriptItems做为主控件的直接嵌套标记。

编译此控件后往页面中添加一个控件,并在属性窗口中增长命令项,而后切换到代码视图会看到以下格式的标记代码:

<cc1:controlbuildercontrol id="ControlBuilderControl1" runat="server">

   <ScriptItems>

       <cc1:ScriptItem>alert('Hello King');</cc1:ScriptItem>

       <cc1:ScriptItem>alert('Hello Rose');</cc1:ScriptItem>         

       <cc1:ScriptItem>alert('Hello James');</cc1:ScriptItem>               

   </ScriptItems>          

</cc1:controlbuildercontrol>

上面生成了一个具备ScriptItem集合属性的标记集合。与上节咱们使用的AddParsedSub Object相比,嵌套标记中多了一个<ScriptItems>内部嵌套标记,且ScriptItem没有前缀和runat属性,若是使用AddParsedSubObject,会把整个<ScriptItems>块(包括其中的ScriptItem一块做为文本LiteralControl返回,这显然不符合咱们的要求;另外,这里的<ScriptItem>虽然具备前缀,但它也不具备runat的属性,但也可以正确被页面解析器进行正反向解析

到目前为止,已经分别使用重载基类AddParsedSubObject方法和继承ControlBuilder的构造实现类实现了自定义的页面解析功能。那么使用它们两个的场景是怎样呢?其实在讲解它们的过程当中笔者已经做了很多比较,再总结一下:

1)在绝大多数状况下,若是页面中只要设置一个内部嵌套标记属性或不须要设置内部嵌套标记属性,则不须要重写AddParsedSubObject和实现ControlBuilder的继承类。这两种方式主要是在实现页面中有多个默认嵌套属性时使用。

2AddParsedSubObject实现比较简单,仅实现一个方法,通常用于复杂属性单一且比较少的状况。

3)实ControlBuilder的定制构造器类比重载AddParsedSubObject要麻烦些,但功能更强,能处理更灵活的嵌套标记。AddParsedSubObject最大的限制是它的内部必须是子控件类型。

4)两种方式都是ASP.NET提供的两种解决方案,都有它们使用的场景,能够根据本身喜爱选择,当习惯使用构造器后,会发现构造器功能更强大、更灵活,用起来更顺手,它能够彻底替代重载AddParsedSubObject方式。

到如今为止基本上已经把咱们见过的全部的属性标记格式都实现了一遍,4.4.3.2节也把平时不多用到的定制页面解析器功能详细地讲解了一下,其中有些标记在日常开发中比较少用到。本章能够做为查找手册使用,何时用到这些内容,何时过来查便可。下一节会有更精彩的内容。

4.5  深刻研究—定制本身的属性编辑器

对于控件的全部属性,若是都提供很是友好的属性编辑器,使用者使用起来会更加方便。本节主旨就是讲解一下控件属性编辑器,4.5.1节提供一些系统通用编辑器;在复杂属性中,集合属性是最重要的最经常使用的属性,4.5.2节将主要讲解怎样定制复杂集合类型编辑器以及一些特殊比较酷的编辑器类型。

这些设计器的执行主要是在设计模式下,直接与IDE交互,在编程时能够直接使用System.Windows命名空间开头的一些命名空间下的类。这里首先加入几个本节须要使用到的引用。右击控件库工程,选择“添加引用”命令,如图4-9所示。

选择“添加引用”命令后会打开“添加引用”对话框,如图4-10所示。

     

 

4-9  添加引用                                          

 

4-10 “添加引用”对话框

 

在对话框中找到如下三个引用程序集:

1System.Designer

2System.Drawing.Design

3System.Windows.Forms

单击“肯定”按钮,这样在须要使用的地方打开程序集中的命名空间,就可使用程序集中的系统类了。

4.5.1  系统属性编辑器

颇有必要用一小节讲解一下系统提供的一些编辑器。读者对这些编辑器可能都比较熟悉,但它们是怎么使用的呢?其实使用都很简单,仅在每一个须要配置的属性前面指定一个标志某种属性编辑器的元数据特性便可。下面就分别介绍一下它们。

4.5.1.1  多行下拉文本属性编辑器

1.配置方式

/// <summary>

/// 得到本书更多内容,请看:

/// http://blog.csdn.net/ChengKing/archive/2008/08/18/2792440.aspx

/// </summary>

[Editor("System.ComponentModel.Design.MultilineStringEditor,System.Design", typeof(UITypeEditor))]

public string TxtEditor

{

    //... ...

}

Editor特性就是指定属性编辑器类型,后面的几种系统属性编辑器类型也是如此。

2.属性浏览器中的效果(如图4-11所示)

 

4-11  多行下拉文本属性编辑器

 

4.5.1.2  色值选择属性编辑器

1.配置方式

[Editor("System.ComponentModel.Design.ColorEditor,System.Design", typeof(UITypeEditor))]

public Color ColorEditor

{

    //... ...

}

2.属性浏览器中的效果(如图4-12所示)

 

4-12  色值选择属性编辑器

 

4.5.1.3  文件选择属性编辑器

1.配置方式

[Editor(typeof(FileNameEditor), typeof(UITypeEditor))]

public string FileName

{

    //... ...

}

2.属性浏览器中的效果(如图4-13所示)

 

4-13  文件选择属性编辑器

上图即为单击属性窗口中“文件名属性”按钮弹出的“文件选择属性编辑器”对话框,其实也是调用的Windows系统的“打开文件”对话框。

4.5.1.4  目录选择属性编辑器

1.配置方式

[Editor(typeof(FolderNameEditor), typeof(UITypeEditor))]

public string FolderNameEditor

{

    //... ...

}

2.属性浏览器中的效果(如图4-14所示)

 

4-14  目录选择属性编辑器

 

4.5.1.5  链接字符串属性编辑器

1.配置方式

[Editor(typeof(System.Web.UI.Design.ConnectionStringEditor), typeof(UITypeEditor))]

public string ConnectionStringEditor

{

    //... ...

}

2.属性浏览器中的效果(如图4-15所示)

 

4-15  链接字符串属性编辑器

 

4.5.1.6  表达式绑定集合属性编辑器

1.配置方式

[Editor(typeof(System.Web.UI.Design.ExpressionsCollectionEditor), typeof(UITypeEditor))]

public string ExpressionsCollectionEditor

{

    //... ...

}

2.属性浏览器中的效果(如图4-16所示)

 

4-16  表达式绑定集合属性编辑器

 

在此窗口能够为属性指定要绑定到应用程序的配置文件、链接字符串或者资源文件。

4.5.1.7  用户控件对话框编辑器

1.配置方式

[Editor(typeof(System.Web.UI.Design.UserControlFileEditor), typeof(UITypeEditor))]

public string UserControlFileEditor

{

    //... ...

}

2.属性浏览器中的效果(如图4-17所示)

 

4-17  用户控件对话框编辑器

 

此窗口用于选择当前站点下的用户控件文件(*.ascx),且默认的可选择路径不像文件和目录选择是本计算机硬盘,而是当前站点。

主控件的完整源代码以下:

 

  1. /// <summary>
  2. /// 得到本书更多内容,请看:
  3. /// http://blog.csdn.net/ChengKing/archive/2008/08/18/2792440.aspx
  4. /// </summary> 
  5. [ToolboxData("<{0}:EditorControl runat=server></{0}:EditorControl>")]
  6. public class EditorControl : WebControl
  7. {
  8.     string strTxtEditor;
  9.     [Category("编辑器")]       
  10.     [Description("下拉多行文本编辑器")] 
  11.     [Editor("System.ComponentModel.Design.MultilineStringEditor, System.Design"typeof(UITypeEditor))]
  12.     public string TxtEditor
  13.     {
  14.         get
  15.         {
  16.             return strTxtEditor;
  17.         }
  18.         set
  19.         {
  20.             strTxtEditor = value;
  21.         }
  22.     }
  23.     Color cColorEditor;
  24.     [Category("编辑器")]
  25.     [Description("颜色编辑器")]
  26.     [Editor("System.ComponentModel.Design.ColorEditor,System.Design"typeof(UITypeEditor))]
  27.     public Color ColorEditor
  28.     {
  29.         get
  30.         {              
  31.             return cColorEditor;
  32.         }
  33.         set
  34.         {
  35.             cColorEditor = value;
  36.         }
  37.     }
  38.     string strFileName;
  39.     [Category("编辑器")]
  40.     [Description("文件选择编辑器")]
  41.     [Editor(typeof(FileNameEditor), typeof(UITypeEditor))]
  42.     public string FileName
  43.     {
  44.         get
  45.         {
  46.              return strFileName;
  47.         }
  48.         set
  49.         {
  50.             strFileName = value;
  51.         }
  52.     }
  53.     string strFolderNameEditor;
  54.     [Category("编辑器")]
  55.     [Description("目录选择编辑器")]
  56.     [Editor(typeof(FolderNameEditor), typeof(UITypeEditor))]
  57.     public string FolderNameEditor
  58.     {
  59.         get
  60.         {
  61.             return strFolderNameEditor;
  62.         }
  63.         set
  64.         {
  65.             strFolderNameEditor = value;
  66.         }
  67.     }
  68.     string strConnectionStringEditor;
  69.     [Category("编辑器")]
  70.     [Description("链接字符串编辑器")]
  71.     [Editor(typeof(System.Web.UI.Design.ConnectionStringEditor), typeof(UITypeEditor))]
  72.     public string ConnectionStringEditor
  73.     {
  74.         get
  75.         {
  76.             return strConnectionStringEditor;
  77.         }
  78.         set
  79.         {
  80.             strConnectionStringEditor = value;
  81.         }
  82.     }
  83.     string strExpressionsCollectionEditor;
  84.     [Category("编辑器")]
  85.     [Description("编辑表达式绑定集合的编辑器")]
  86.     [Editor(typeof(System.Web.UI.Design.ExpressionsCollectionEditor), typeof(UITypeEditor))]
  87.     public string ExpressionsCollectionEditor
  88.     {
  89.         get
  90.         {
  91.             return strExpressionsCollectionEditor;
  92.         }
  93.         set
  94.         {
  95.             strExpressionsCollectionEditor = value;
  96.         }
  97.     }
  98.     string strUserControlFileEditor;
  99.     [Category("编辑器")]
  100.     [Description("用户控件(ascx)对话框编辑器")]
  101.     [Editor(typeof(System.Web.UI.Design.UserControlFileEditor), typeof(UITypeEditor))]
  102.     public string UserControlFileEditor
  103.     {
  104.         get
  105.         {
  106.             return strUserControlFileEditor;
  107.         }
  108.         set
  109.         {
  110.             strUserControlFileEditor = value;
  111.         }
  112.     }
  113. //... ...
  114. }

这些代码比较简单,就不做解释了。以上仅列出一些可能会常常用到的属性编辑器,系统提供的属性编辑器不止这些,像前面讲的集合也是使用系统默认的集合属性编辑器。其余的属性编辑器能够在使用的过程当中慢慢研究。下一节经过几个例子详细讲一下怎样定制本身的属性编辑器。

4.5.2  定制属性编辑器

提供了不少属性编辑器,可以知足绝大多数复杂度不是很高的控件的须要。这一节主要经过四个小例子讲解怎样定制个性化的属性编辑器,其实只要可以想到,咱们就可以作到。

4.5.2.1  定制多类型集合属性编辑器

前面咱们在为控件增长集合属性时,默认状况下会使用系统默认的集合编辑器,而咱们这里自定义的集合属性编辑器功能更强,先看一下它实现后的效果图,如图4-18所示。

能够看到,该编辑器除了可以实现基本的集合属性编辑功能外,经过单击“添加”按钮右边的下拉按钮还能够选择添加项的类型,即咱们能够定义任意个不一样类型的集合项做为属性集合内容,这里咱们定义两个集合子项类别:CommonItem(子项)和CommandSeperator(分隔符),以它们做为示例讲解。下面就来看一下它的实现过程。

 

4-18  定制多类型集合属性编辑器

 

因为涉及集合,首先仍是定义几个与集合实现相关的类。定义一个抽象类ItemBase,表示每一个集合子项类的子类,任何集合子类都要继承该类。其类代码以下所示:

 

  1. /// <summary>
  2. /// 得到本书更多内容,请看:
  3. /// http://blog.csdn.net/ChengKing/archive/2008/08/18/2792440.aspx
  4. /// </summary> 
  5. /// <summary>
  6. /// 命令项基类
  7. /// </summary>
  8. public abstract class ItemBase
  9. {
  10.     private bool _EnableViewState = true;
  11.     public bool EnableViewState
  12.     {
  13.         get
  14.         {
  15.             return _EnableViewState;
  16.         }
  17.         set
  18.         {
  19.             _EnableViewState = value;
  20.         }
  21.     }
  22. }

这里为了简化代码,只定义了一个EnableViewState的属性,表示是否启用视图状态,在使用时还能够继承Control等基类,使用Control等类的类成员。这里要注意的是此类定义成了抽象类,此类不会单独生成实例添加到集合中,也不会被做为一种集合子类型显示到属性编辑窗口中供用户选择,由于ItemBase在这里没有表示具体的集合子项,也不具备集合子项意义。全部集合子类型最终是以它们的基类型ItemBase添加到集合中统一管理的。

下面定义一个具体的集合子项类CommonItem,表示一个按钮类型,类结构代码以下:

 

  1. /// <summary>
  2. /// 得到本书更多内容,请看:
  3. /// http://blog.csdn.net/ChengKing/archive/2008/08/18/2792440.aspx
  4. /// </summary> 
  5. /// <summary>
  6. ///  命令按钮类
  7. /// </summary>    
  8. [ToolboxItem(false)]    
  9. public class CommandItem : ItemBase
  10. {
  11.     private CommandActionType _CommandActionType;
  12.     //命令按钮文本
  13.     private string _Text = null;
  14.     //快捷键
  15.     private string _AccessKey = null;
  16.     //提示
  17.     private string _ToolTip = null;
  18.     //是否可用
  19.     private bool _Enable = true;        
  20.     /// <summary>
  21.     /// 默认构造方法
  22.     /// </summary>        
  23.     public CommandItem()
  24.     {
  25.     }
  26.     /// <summary>
  27.     /// 构造方法[ButtonCommand]
  28.     /// </summary>
  29.     /// <param name="bitButtonItemType"></param>        
  30.     /// <param name="strCommandText"></param>
  31.     /// <param name="strAccessKey"></param>
  32.     /// <param name="strToolTip"></param>        
  33.     public CommandItem(CommandActionType commandActionType, string strText, string strAccessKey, string strToolTip)
  34.     {
  35.         this._CommandActionType = commandActionType;
  36.         this._Text = strText;
  37.         this._AccessKey = strAccessKey;
  38.         this._ToolTip = strToolTip;            
  39.     }
  40.     /// <summary>
  41.     /// 命令按钮类型
  42.     /// </summary>
  43.     [NotifyParentProperty(true)]
  44.     public CommandActionType CommandActionType
  45.     {
  46.         get
  47.         {
  48.             return _CommandActionType;
  49.         }
  50.         set
  51.         {
  52.             _CommandActionType = value;
  53.         }
  54.     }
  55.     /// <summary>
  56.     /// 命令按钮文本
  57.     /// </summary>
  58.     [NotifyParentProperty(true)]
  59.     [Browsable(false)]
  60.     public string Text
  61.     {
  62.         get
  63.         {
  64.             return _Text;
  65.         }
  66.         set
  67.         {
  68.             _Text = value;
  69.         }
  70.     }
  71.     /// <summary>
  72.     /// 快捷键
  73.     /// </summary>
  74.     [NotifyParentProperty(true)]
  75.     [Browsable(false)]
  76.     public string AccessKey
  77.     {
  78.         get
  79.         {
  80.             return _AccessKey;
  81.         }
  82.         set
  83.         {
  84.             _AccessKey = value;
  85.         }
  86.     }
  87.     /// <summary>
  88.     /// 帮助提示文本
  89.     /// </summary>
  90.     [NotifyParentProperty(true)]
  91.     [Browsable(false)]
  92.     public string ToolTip
  93.     {
  94.         get
  95.         {
  96.             return _ToolTip;
  97.         }
  98.         set
  99.         {
  100.             _ToolTip = value;
  101.         }
  102.     }
  103.     /// <summary>
  104.     /// 是否可用
  105.     /// </summary>
  106.     [NotifyParentProperty(true)]
  107.     [Browsable(false)]
  108.     public bool Enable
  109.     {
  110.         get
  111.         {
  112.             return _Enable;
  113.         }
  114.         set
  115.         {
  116.             _Enable = value;
  117.         }
  118.     }
  119. }

类代码很简单,主要包括描述按钮的一些基本信息:文本、快捷键、提示、可用性以及按钮类型(新增/保存/删除等)。这里仅须要注意CommandItem类继承了上面咱们定义的抽象集合子项基类ItemBase,全部类型的子项都要继承于该类。

CommandItem的第一个属性是枚举类型,表示此按钮的功能类型(新增/删除/上一页/下一页等),此属性CommandActionType对应的枚举代码结构以下所示:

/// <summary>

/// 得到本书更多内容,请看:

/// http://blog.csdn.net/ChengKing/archive/2008/08/18/2792440.aspx

/// </summary>

 

/// <summary>

/// 命令项枚举

/// </summary>

public enum CommandActionType

{

    //保存

    Save,

 

    //新增

    Add,

 

    //编辑

    Edit,

 

    //删除

    Delete,

 

    //关闭

    Close

 

    //...

}

 

u  接下来再定义一个子项类型:CommandSeperator类型,表示分隔符类型,即一组按钮与另外一组按钮之间的分隔符。好比:“首页”、“上一页”、“下一页”、“末页”,这是一组具备相似功能的一级按钮。另外一组:“新增”、“修改”、“删除”、“查看”属于一组功能相似按钮,这两组按钮之间须要用某个分隔符分开,这样可使使用者更容易区分各个按钮的功能,外观布局也不会显示零乱。类代码以下所示:

 

  1. /// <summary>
  2. /// 得到本书更多内容,请看:
  3. /// http://blog.csdn.net/ChengKing/archive/2008/08/18/2792440.aspx
  4. /// </summary> 
  5. /// <summary>
  6. /// 分隔符类
  7. /// </summary>
  8. [ToolboxItem(false)]
  9. public class CommandSeperator : ItemBase
  10. {
  11.     private Unit width;
  12.     private Unit Width
  13.     {
  14.         get
  15.         {
  16.             return width;
  17.         }
  18.         set
  19.         {
  20.             width = value;
  21.         }
  22.     }
  23.     private Unit height;
  24.     private Unit Height
  25.     {
  26.         get
  27.         {
  28.             return height;
  29.         }
  30.         set
  31.         {
  32.             height = value;
  33.         }
  34.     }
  35. }

此分隔符类仅包括两个属性:宽度和高度。另外,它也继承了ItemBase抽象类。

到如今为止,已经定义完四个类:抽象基类(ItemBase),按钮类(CommandItem),分隔按钮类(CommandSeperator),以及一个功能枚举类(CommandActionType)。而后就能够定义一个存储以上各个按钮类型的集合类了,类名为CommandCollection,此集合为强类型集合类,类代码以下:

 

  1. /// <summary>
  2. /// 得到本书更多内容,请看:
  3. /// http://blog.csdn.net/ChengKing/archive/2008/08/18/2792440.aspx
  4. /// </summary> 
  5. /// <summary>
  6. /// 工具按钮集合类
  7. /// </summary>
  8. [ToolboxItem(false)]
  9. [ParseChildren(true)]
  10. [Editor(typeof(CommandCollectionEditor), typeof(UITypeEditor))]
  11. public class CommandCollection : Collection<ItemBase>
  12. {
  13.     #region 定义构造函数
  14.     public CommandCollection()
  15.         : base()
  16.     {
  17.     }
  18.     #endregion
  19.     /// <summary>
  20.     /// 获得集合元素的个数
  21.     /// </summary>
  22.     public new int Count
  23.     {
  24.         get
  25.         {
  26.             return base.Count;
  27.         }
  28.     }
  29.     /// <summary>
  30.     /// 表示集合是否为只读
  31.     /// </summary>
  32.     public bool IsReadOnly
  33.     {
  34.         get
  35.         {
  36.             return false;
  37.         }
  38.     }
  39.     /// <summary>
  40.     /// 添加对象到集合
  41.     /// </summary>
  42.     /// <param name="item"></param>
  43.     public new void Add(ItemBase item)
  44.     {
  45.         base.Add(item);
  46.     }
  47.     /// <summary>
  48.     /// 清空集合
  49.     /// </summary>
  50.     public new void Clear()
  51.     {
  52.         base.Clear();
  53.     }
  54.     /// <summary>
  55.     /// 判断集合中是否包含元素
  56.     /// </summary>
  57.     /// <param name="item"></param>
  58.     /// <returns></returns>
  59.     public new bool Contains(ItemBase item)
  60.     {
  61.         return base.Contains(item);
  62.     }
  63.     /// <summary>
  64.     /// 移除一个对象
  65.     /// </summary>
  66.     /// <param name="item"></param>
  67.     /// <returns></returns>
  68.     public new bool Remove(ItemBase item)
  69.     {
  70.         return base.Remove(item);
  71.     }
  72.     /// <summary>
  73.     /// 设置或取得索引项
  74.     /// </summary>
  75.     /// <param name="index"></param>
  76.     /// <returns></returns>
  77.     public new ItemBase this[int index]
  78.     {
  79.         get
  80.         {
  81.             return base[index];
  82.         }
  83.         set
  84.         {
  85.             base[index] = value;
  86.         }
  87.     }        
  88. }

该集合类继承Collection<ItemBase>类,表示强类型集合,且每一个子项的类型为ItemBase,从这里能够想到咱们上面定义的两个子项类CommandItemCommandSeperator都要继承于ItemBase的缘由了。[ParseChilderen(true)]表示把当前属性做为主控件的属性(而非子控件)进行解析。

本节的重点,也是最重要的一个属性[Editor(typeof(CommandCollectionEditor)typeof (UITypeEditor))]表示指定此集合类的集合编辑器为CommandCollectionEditor,即在主控件中凡是定义为CommandCollection类的属性都会把CommandCollectionEditor做为它的编辑器。下面详细介绍一下编辑器类是怎么使用的。仍是先看一下 CommandCollectionEditor编辑器类的源代码:

 

  1. /// <summary>
  2. /// 得到本书更多内容,请看:
  3. /// http://blog.csdn.net/ChengKing/archive/2008/08/18/2792440.aspx
  4. /// </summary> 
  5. /// <summary>
  6. /// 集合属性编辑器
  7. /// </summary>
  8. public class CommandCollectionEditor : CollectionEditor
  9. {
  10.     public CommandCollectionEditor(Type type)
  11.         : base(type)
  12.     { }
  13.     protected override bool CanSelectMultipleInstances()
  14.     {
  15.         return true;
  16.     }
  17.     protected override Type[] CreateNewItemTypes()
  18.     {            
  19.         return new Type[] { typeof(CommandItem), typeof(CommandSeperator) };
  20.     }
  21.     protected override object CreateInstance(Type itemType)
  22.     {
  23.         if (itemType == typeof(CommandItem))
  24.         {
  25.             return new CommandItem();
  26.         }
  27.         if (itemType == typeof(CommandSeperator))
  28.         {
  29.             return new CommandSeperator();
  30.         }
  31.         return null;
  32.     }
  33. }

实现一个集合编辑器通常要继承System.ComponentModel.Design.CollectionEditor,并重写该类的一些方法。下面分别说一下各个方法的做用。

Ø 集合编辑器中的构造方法 CommandCollectionEditor ,主要完成自定义的初始化功能。该方法中的参数返回该编辑器做用的对象实例(在这里是CommandCollection的一个对象实例),能够取到当前CommandCollection对象的全部数据。

Ø 方法CanSelectMultipleInstances的返回值表示是否可以在编辑窗口选择多个实例,这里设置返回true

Ø 重写方法CreateNewItemTypes,返回咱们定义的两个集合类型:

return new Type[] { typeof(CommandItem), typeof(CommandSeperator) };

CommandItemCommandSeperator是咱们定义的两个集合类型。在单击主控件属性窗口中集合属性编辑器“”形状按钮时,此方法执行,把全部定义的类型加载到系统集合中缓存起来,而后根据此集合值在编辑器界面中呈现可能选择类型的列表。

Ø CreateInstance方法主要是负责创建一个集合子项类型实例。

至此全部功能类都创建完成了,最后创建主控件类,并应用CommandCollection集合类做为控件的一个属性,代码以下所示:

 

  1. /// <summary>
  2. /// 得到本书更多内容,请看:
  3. /// http://blog.csdn.net/ChengKing/archive/2008/08/18/2792440.aspx
  4. /// </summary> 
  5. [DefaultProperty("ToolBarItems")]
  6. [ToolboxData("<{0}:MultiTypeCollectionEditorControl runat=server></{0}: MultiTypeCollectionEditorControl>")]
  7. [ParseChildren(true"ToolBarItems")]
  8. public class MultiTypeCollectionEditorControl : WebControl
  9. {
  10.     private CommandCollection _ToolBarItems = new CommandCollection();
  11.     [PersistenceMode(PersistenceMode.InnerProperty)]
  12.     [DesignerSerializationVisibility(DesignerSerializationVisibility. Content)]
  13.     [Description("工具按钮集设置")]
  14.     [Category("集合设置")]
  15.     public CommandCollection ToolBarItems
  16.     {
  17.         get
  18.         {
  19.             if (_ToolBarItems == null)
  20.             {
  21.                 _ToolBarItems = new CommandCollection();
  22.             }         
  23.             return _ToolBarItems;
  24.         }
  25.     }
  26. //....
  27. }

主控件定义了一个重要的类元数据特性[ParseChildren(true"ToolBarItems")],表示把ToolBarItems做为控件的属性进行解析。其余属性在前面的章节已经讲解屡次了,这里就再也不赘述。

编译控件源代码库,在页面设计器中置一个控件,而后单击属性窗口中对应的集合属性,会打开咱们刚刚定义的集合属性编辑器,如图4-19所示。

 

4-19  集合编辑器

 

在这里就能够选择命令按钮或分隔符按钮填充集合了。另外,在成员列表中若是不想看到带命名空间的类名项,好比只让它显示CommandItem而不是KingControls.CommandItem,只要为其增长一个类型转换器便可。后面4.6节会详细讲解类型转换器的实现。这个功能比较简单,若是须要,读者能够本身实现它。

好了,本节内容已经讲解完,在讲解过程当中使用到了不少类,这些类都是在实际开发中经常使用的一些类,只是限于篇幅笔者把它们都精简化了,读者也能够体会一下它们的用途。

4.5.2.2  定制模态属性编辑器

这一节咱们学习定制另外一种属性编辑器:模态编辑器,在此编辑器中单击一个按钮将弹出一个窗体,从窗体选择数据后会把值返回到属性窗口中。最重要的一点是咱们能够自定义此选择数据的模态窗口内容,比上面的集合编辑器更灵活。仍是先看一下效果图,如图4-20所示。

 

4-20  模态属性编辑器

 

上图是以一个表示选择食品(水果/肉类/蔬菜等)的属性为例而定制的一个模态选择窗口,单击属性旁边的“”按钮就会弹出图中左侧的模态数据选择窗口。

下面就来讲一下它是怎么实现的。首先要说明的是因为在设计模式下且模态是直接供IDE接口调用的,所以这里弹出的窗口就是一个很是普通的WinForm窗口。在咱们控件中新增一个WinForm文件CategoryWindow.cs,如图4-21所示。

 

4-21 “添加新项”对话框

 

增长完后,放置一个ComboBox(提供绑定食品类别数据的选择列表)和两个Button控件(“肯定”和“取消”)到窗体中,再在“肯定”按钮的事件中增长数据返回功能。

系统会把窗体类分红两个部分类文件:CategoryWindow.csategoryWindow.Designer.csCategoryWindow.Designer.cs主要存储窗体和内部控件内容信息;CategoryWindow.cs主要供开发人员完成交互逻辑使用。下面分别来看一下它们的源代码。

1CategoryWindow.Designer.cs文件中窗体部分的类代码

/// <summary>

/// 得到本书更多内容,请看:

/// http://blog.csdn.net/ChengKing/archive/2008/08/18/2792440.aspx

/// </summary>

 

partial class CategoryWindow

{

    /// <summary>

    /// 必需的设计器变量。

    /// </summary>

    private System.ComponentModel.IContainer components = null;

 

    /// <summary>

    /// 清理全部正在使用的资源。

    /// </summary>

    /// <param name="disposing">若是应释放托管资源,为 true;不然为 false</param>

    protected override void Dispose(bool disposing)

    {

        if (disposing && (components != null))

        {

            components.Dispose();

        }

        base.Dispose(disposing);

    }

 

    #region Windows 窗体设计器生成的代码

 

    /// <summary>

    /// 设计器支持所需的方法 - 不要

    /// 使用代码编辑器修改此方法的内容。

    /// </summary>

    private void InitializeComponent()

    {

        this.comboBox1 = new System.Windows.Forms.ComboBox();

        this.button1 = new System.Windows.Forms.Button();

        this.button2 = new System.Windows.Forms.Button();

        this.SuspendLayout();

        //

        // comboBox1

        //

        this.comboBox1.FormattingEnabled = true;

        this.comboBox1.Location = new System.Drawing.Point(23, 12);

        this.comboBox1.Name = "comboBox1";

        this.comboBox1.Size = new System.Drawing.Size(217, 20);

        this.comboBox1.TabIndex = 0;

        //

        // button1

        //

        this.button1.DialogResult = System.Windows.Forms.DialogResult.OK;

        this.button1.Location = new System.Drawing.Point(84, 53);

        this.button1.Name = "button1";

        this.button1.Size = new System.Drawing.Size(75, 23);

        this.button1.TabIndex = 1;

        this.button1.Text = "肯定";

        this.button1.UseVisualStyleBackColor = true;            

        //

        // button2

        //

        this.button2.DialogResult = System.Windows.Forms.DialogResult.Cancel;

        this.button2.Location = new System.Drawing.Point(165, 53);

        this.button2.Name = "button2";

        this.button2.Size = new System.Drawing.Size(75, 23);

        this.button2.TabIndex = 2;

        this.button2.Text = "取消";

        this.button2.UseVisualStyleBackColor = true;

        //

        // CategoryWindow

        //

        this.AcceptButton = this.button1;

        this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);

        this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;

        this.ClientSize = new System.Drawing.Size(252, 96);

        this.Controls.Add(this.button2);

        this.Controls.Add(this.button1);

        this.Controls.Add(this.comboBox1);

        this.Cursor = System.Windows.Forms.Cursors.Default;

        this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.Fixed3D;

        this.MaximizeBox = false;

        this.MinimizeBox = false;

        this.Name = "CategoryWindow";

        this.StartPosition=System.Windows.Forms.FormStartPosition.CenterScreen;

        this.Text = "CategoryWindow";

        this.TopMost = true;

        this.ResumeLayout(false);

 

    }

 

    #endregion      

 

    public System.Windows.Forms.ComboBox comboBox1;

    private System.Windows.Forms.Button button1;

    private System.Windows.Forms.Button button2;

}

u  须要说明的一点是上面代码中把两个Button设置为窗体返回结果的枚举值,以下:

this.button1.DialogResult = System.Windows.Forms.DialogResult.OK;

this.button2.DialogResult = System.Windows.Forms.DialogResult.Cancel;

以上语句表示:单击“肯定”按钮则返回选中的数据给父窗体;单击“取消”按钮则不返回数据。

2CategoryWindow.cs 文件中窗体部分的类代码

 

  1. /// <summary>
  2. /// 得到本书更多内容,请看:
  3. /// http://blog.csdn.net/ChengKing/archive/2008/08/18/2792440.aspx
  4. /// </summary> 
  5. public partial class CategoryWindow : Form
  6. {
  7.     public CategoryWindow()
  8.     {
  9.         InitializeComponent();
  10.         SetSelectData();
  11.     }
  12.     public void SetSelectData()
  13.     {
  14.         try
  15.         {
  16.             this.comboBox1.Items.Add("水果");
  17.             this.comboBox1.Items.Add("蔬菜");
  18.             this.comboBox1.Items.Add("肉类");
  19.             this.comboBox1.Items.Add("蛋类");
  20.             this.comboBox1.Items.Add("面食");
  21.         }
  22.         catch (Exception eee)
  23.         {
  24.             throw eee;
  25.         }
  26.         finally
  27.         {
  28.         }
  29.     }  
  30. }

该页面没有复杂的交互逻辑,仅在类构造方法中调用SetSelectData方法为窗体中的ComboBox控件绑定食品数据列表。这里限于篇幅仅作了一个尽可能简单的窗体,在实际开发中还能够定制任意复杂的窗体,在本节最后还提供了一个比较复杂的能够实现计算器功能的模态窗体。

数据选择窗体已经创建好以后,再建立控件的属性编辑器文件,该编辑器文件中的类主要用于调用上面建立的数据选择窗体,包括打开窗体,选择完数据后,接收值并赋给属性窗口的对应属性。在讲解源代码以前,要先打开几个命名空间:

using System.Drawing;

using System.Drawing.Design;

using System.Windows.Forms;

using System.Windows.Forms.Design;

u  这些命名空间主要是提供控件对WinForm的设计模式支持。下面仍是先看一下此编辑器类的代码:

 

  1. /// <summary>
  2. /// 得到本书更多内容,请看:
  3. /// http://blog.csdn.net/ChengKing/archive/2008/08/18/2792440.aspx
  4. /// </summary> 
  5. public class CategoryModalEditor : System.Drawing.Design.UITypeEditor
  6. {
  7.     public CategoryModalEditor()
  8.     {
  9.     }
  10.     public override System.Drawing.Design.UITypeEditorEditStyle GetEditStyle (System.ComponentModel.ITypeDescriptorContext context)
  11.     {            
  12.         return UITypeEditorEditStyle.Modal;
  13.     }
  14.     public override object EditValue(System.ComponentModel.ItypeDescriptor Context context, System.IServiceProvider provider, object value)
  15.     {
  16.         IWindowsFormsEditorService service = (IWindowsFormsEditorService) provider.GetService(typeof(IWindowsFormsEditorService));
  17.         if (service == null)
  18.         {
  19.             return null;
  20.         }
  21.         
  22.         CategoryWindow form = new CategoryWindow();            
  23.         if (service.ShowDialog(form) == DialogResult.OK)
  24.         {
  25.             return form.comboBox1.SelectedItem; 
  26.         }
  27.         
  28.         return value;
  29.     }
  30. }

这里使用的编辑器基类与前面咱们定义的集合编辑器不同,前面集合编辑器是使用System.ComponentModel.Design下的集合基类,这里使用的是System.Drawing.Design下的UItypeEdit基类。在实际开发中能够任意选择系统提供的编辑器基类,在4.5.1节已经列出了不少基类,也能够直接继承这些类定制本身的编辑器。

方法GetEditStyleSystem.ComponentModel.ITypeDescriptorContext类型参数,表示要转换的对象的上下文;方法GetEditStyleUITypeEditorEditStyle.ModalUITypeEditorEditStyle枚举表示以什么样的形式打开编辑窗体,它有三个枚举值:ModalDropDownNone。其中Modal表示以模态形式弹出编辑属性界面;DropDown表示如下拉形式显示属性编辑界面;None表示不提供任何形式的UI界面。这里咱们选择的是Modal枚举值,表示以模态形式弹出上面咱们创建好的食品类别选择窗体。

EditValue方法是主要的属性编辑方法,当单击属性窗口中的属性按钮时会执行此方法。它有三个参数:第一个参数表示当前上下文对象,能够今后对象得到当前父窗口和属性的设计时元数据信息等;第二个参数是服务提供者对象,能够根据此对象获取当前咱们须要的服务;第三个参数为当前属性的默认值,即编辑当前属性以前的值。好比:

IWindowsFormsEditorService service = (IWindowsFormsEditorService)provider. GetService(typeof(IWindowsFormsEditorService));

u  以上语句表示获取IwindowsFormsEditorService类型的服务对象(专门为WinForm窗体编辑器提供一些功能),获取对象后就可使用服务的方法:

if (service.ShowDialog(form) == DialogResult.OK)

{

    return form.comboBox1.SelectedItem;

}

ShowDialog就是IWindowsformsEditorService类型对象的一个方法,表示打开form对象窗体。另外,ShowDialog还有一个DialogResult枚举的返回值,这里表示若是返回值为OK枚举项,才真正把窗体中当前ComboBoxSelectedItem项返回。看到这里,咱们可能会想起前面把数据选择窗体中的“肯定”和“取消”两个按钮的DialogResult属性值分别设置为DialogResult.OKDialogResult.Cancel的用途了。

本方法中第三个参数表示当前属性窗口中对应属性的当前值。有时您能够根据此值写一些相关的交互逻辑,好比根据此值设置弹出窗口的默认选中项(该功能比较简单,您能够扩展该控件功能,本身去实现它)。

整个EditValue方法返回一个object类型的值,系统会根据此返回值对属性窗口进行填充。

整个数据选择就是这样的一个过程,最后咱们在主控件代码中对上面定义的数据选择窗体和编辑器进行应用。主控件源代码以下:

/// <summary>

/// 得到本书更多内容,请看:

/// http://blog.csdn.net/ChengKing/archive/2008/08/18/2792440.aspx

/// </summary>

 

[DefaultProperty("SelectFood")]

[ToolboxData("<{0}:CustomeModalEditorControl runat=server></{0}: CustomeModalEditorControl>")]

public class CustomeModalEditorControl : WebControl

{

    [Bindable(true)]

    [Category("类别")]

    [DefaultValue("")]

    [Localizable(true)]

    [Editor(typeof(CategoryModalEditor), typeof(System.Drawing.Design.UIType Editor))]

    [Description("选择食品类别")]

    public string SelectFood

    {

        get

        {

            String s = (String)ViewState["SelectFood"];

            return ((s == null) ? String.Empty : s);

        }

 

        set

        {

            ViewState["SelectFood"] = value;

        }

    }

    //… …

}

 

u  主控件中有个选择食品类别的属性SelectFood,该属性上面有一句:

[Editor(typeof(CategoryModalEditor),typeof(System.Drawing.Design.UITypeEditor))]

该句代码指定属性的编辑器为CategoryModalEditor。最后,编译控件,在属性窗口中便可看到咱们定义的属性,如图4-22所示。

 

4-22  模态属性编辑器

 

4.5.2.3  定制下拉控件属性编辑器

这一节咱们再学习定制另外一种形式的属性编辑器:下拉编辑器,单击按钮会下拉一个控件,当使用者从控件选择数据后该数据值被返回到属性窗口中。而且此选择数据的模态窗口内容也是能够自定义的。仍是先看一下效果图,如图4-23所示。

上图也是以一个表示选择食品(水果/肉类/蔬菜等)属性为例而定制的编辑器示例,单击属性旁边的下拉按钮会下拉一个数据选择的界面,而且此界面也是能够任意定制的。

下面就来讲一下此编辑器是怎么实现的,在控件中新增一个用户控件(此次不是Windows窗体),如图4-24所示。

  

4-23  下拉控件属性编辑器                                    

 

4-24  添加新项对话框

 

而后放置一个ComboBox(提供绑定食品类别数据的选择列表)和两个Button控件(“肯定”和“取消”)到窗体中,再在“肯定”按钮的事件中增长数据返回功能。

增长用户控件文件后,系统会把窗体类分红两个部分类文件:CategoryDropDown.csCategoryDropDown.Designer.csCategoryDropDown.cs主要供开发人员完成交互逻辑;CategoryDropDown.Designer.cs主要存储窗体和内部控件内容信息,下面分别来看一下它们的源代码。

1CategoryDropDown.Designer.cs文件中用户控件部分类的代码

 

  1. /// <summary>
  2. /// 得到本书更多内容,请看:
  3. /// http://blog.csdn.net/ChengKing/archive/2008/08/18/2792440.aspx
  4. /// </summary> 
  5. partial class CategoryDropDown
  6. {
  7.     /// <summary> 
  8.     /// 必需的设计器变量。
  9.     /// </summary>
  10.     private System.ComponentModel.IContainer components = null;
  11.     /// <summary> 
  12.     /// 清理全部正在使用的资源。
  13.     /// </summary>
  14.     /// <param name="disposing">若是应释放托管资源,为 true;不然为 false。</param>
  15.     protected override void Dispose(bool disposing)
  16.     {
  17.         if (disposing && (components != null))
  18.         {
  19.             components.Dispose();
  20.         }
  21.         base.Dispose(disposing);
  22.     }
  23.     #region 组件设计器生成的代码
  24.     /// <summary> 
  25.     /// 设计器支持所需的方法 - 不要
  26.     /// 使用代码编辑器修改此方法的内容。
  27.     /// </summary>
  28.     private void InitializeComponent()
  29.     {
  30.         this.btnCancel = new System.Windows.Forms.Button();
  31.         this.btnOK = new System.Windows.Forms.Button();
  32.         this.comboBox1 = new System.Windows.Forms.ComboBox();
  33.         this.SuspendLayout();
  34.         // 
  35.         // btnCancel
  36.         // 
  37.         this.btnCancel.DialogResult=System.Windows.Forms.DialogResult.Cancel;
  38.         this.btnCancel.Location = new System.Drawing.Point(161, 56);
  39.         this.btnCancel.Name = "btnCancel";
  40.         this.btnCancel.Size = new System.Drawing.Size(75, 23);
  41.         this.btnCancel.TabIndex = 5;
  42.         this.btnCancel.Text = "取消";
  43.         this.btnCancel.UseVisualStyleBackColor = true;
  44.         this.btnCancel.Click+=new System.EventHandler(this.btnCancel_Click);
  45.         // 
  46.         // btnOK
  47.         // 
  48.         this.btnOK.DialogResult = System.Windows.Forms.DialogResult.OK;
  49.         this.btnOK.Location = new System.Drawing.Point(80, 56);
  50.         this.btnOK.Name = "btnOK";
  51.         this.btnOK.Size = new System.Drawing.Size(75, 23);
  52.         this.btnOK.TabIndex = 4;
  53.         this.btnOK.Text = "肯定";
  54.         this.btnOK.UseVisualStyleBackColor = true;
  55.         this.btnOK.Click += new System.EventHandler(this.btnOK_Click);
  56.         // 
  57.         // comboBox1
  58.         // 
  59.         this.comboBox1.FormattingEnabled = true;
  60.         this.comboBox1.Location = new System.Drawing.Point(19, 15);
  61.         this.comboBox1.Name = "comboBox1";
  62.         this.comboBox1.Size = new System.Drawing.Size(217, 20);
  63.         this.comboBox1.TabIndex = 3;
  64.         // 
  65.         // CategoryDropDown
  66.         // 
  67.         this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);
  68.         this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
  69.         this.Controls.Add(this.btnCancel);
  70.         this.Controls.Add(this.btnOK);
  71.         this.Controls.Add(this.comboBox1);
  72.         this.Name = "CategoryDropDown";
  73.         this.Size = new System.Drawing.Size(254, 95);
  74.         this.ResumeLayout(false);
  75.     }
  76.     #endregion
  77.     private System.Windows.Forms.Button btnCancel;
  78.     private System.Windows.Forms.Button btnOK;
  79.     public System.Windows.Forms.ComboBox comboBox1;
  80. }

这里没有像模态编辑器示例同样设置控件的DialogResult属性,而是换了一种方式,分别为“肯定”和“取消”两按钮定义事件,在事件中进行数据返回逻辑处理,关于事件将在接下来要讲解的另外一个部分类中介绍。

2CategoryDropDown.cs文件中用户控件部分类的代码

/// <summary>

/// 得到本书更多内容,请看:

/// http://blog.csdn.net/ChengKing/archive/2008/08/18/2792440.aspx

/// </summary>

 

public partial class CategoryDropDown : UserControl

{

    public string strReturnValue = "";

 

    private IWindowsFormsEditorService service=null;

    public CategoryDropDown(IWindowsFormsEditorService service)

    {

        InitializeComponent();

        SetSelectData();

        this.service = service;

    }

 

    public void SetSelectData()

    {

        try

        {

            this.comboBox1.Items.Add("水果");

            this.comboBox1.Items.Add("蔬菜");

            this.comboBox1.Items.Add("肉类");

            this.comboBox1.Items.Add("蛋类");

            this.comboBox1.Items.Add("面食");

        }

        catch (Exception eee)

        {

            throw eee;

        }

        finally

        {

 

        }

    }

 

    private void btnOK_Click(object sender, EventArgs e)

    {

        strReturnValue = this.comboBox1.SelectedItem.ToString();

        service.CloseDropDown();

    }

 

    private void btnCancel_Click(object sender, EventArgs e)

    {

        strReturnValue = "";

        service.CloseDropDown();

    }

}

 

u  4.5.2.2节讲的模态编辑器同样构造函数中的SetSelectData方法是提供控件ComboBox下拉界面中的食品类别数据列表另外构造函数中多了一个IWindowsFormsEditorService类型的参数(在4.5.2.2节对该类型进行了说明)把窗体编辑器服务对象传递过来由于这里咱们要在“肯定”和“取消”事件中调用关闭当前界面的方法:

service.CloseDropDown();

编辑器的EditValue方法负责打开下拉界面在下拉界面中的两个按钮中要分别调用关闭本身的代码

单击“肯定”按钮时会把当前选择的值赋给类内部变量strReturnValue在下面讲的编辑器中会获取该变动的值并赋值到属性

至此下拉界面已经定义完成接下来定义编辑器类代码以下:

 

  1. /// <summary>
  2. /// 得到本书更多内容,请看:
  3. /// http://blog.csdn.net/ChengKing/archive/2008/08/18/2792440.aspx
  4. /// </summary> 
  5. public class CategoryDropDownEditor : System.Drawing.Design.UITypeEditor
  6. {
  7.     public CategoryDropDownEditor()
  8.     {
  9.     }
  10.     public override System.Drawing.Design.UITypeEditorEditStyle GetEditStyle (System.ComponentModel.ITypeDescriptorContext context)
  11.     {
  12.         //指定编辑样式为下拉形状, 且基于Control类型
  13.         return UITypeEditorEditStyle.DropDown;
  14.     }
  15.     public override object EditValue(System.ComponentModel.ItypeDescriptor Context context, System.IServiceProvider provider, object value)
  16.     {
  17.         //取得编辑器服务对象
  18.         IWindowsFormsEditorService service = (IWindowsFormsEditorService) provider.GetService(typeof(IWindowsFormsEditorService));
  19.         if (service == null)
  20.         {
  21.             return null;
  22.         }
  23.         //定义一个用户控件对象
  24.         CategoryDropDown form = new CategoryDropDown(service);
  25.         service.DropDownControl(form);        
  26.     
  27.         string strReturn = form.strReturnValue;
  28.         if (strReturn + String.Empty != String.Empty)
  29.         {
  30.             return strReturn;
  31.         }
  32.         return (string)value;      
  33.     }
  34. }

下拉编辑器与前面小节讲的弹出式模态编辑器都经过继承System.Drawing.Design. UITypeEditor类实现定制编辑器。

GetEditStyle方法中的代码:

return UITypeEditorEditStyle.DropDown;

指定编辑器类型为下拉模式。

EditValue中主要建立数据选择界面并如下拉模式打开。下面说一下它内部代码实现逻辑。

IWindowsFormsEditorService service = (IWindowsFormsEditorService)provider. GetService(typeof(IWindowsFormsEditorService));

u  以上代码主要获取窗体编辑对象的服务对象,在本例中用到了它的下拉和关闭数据选择界面的方法。

CategoryDropDown form = new CategoryDropDown(service);

service.DropDownControl(form);

u  以上代码主要是建立一个数据选择界面(用户控件类型),并使用service对象的DropDownControl 方法把参数指定的用户控件如下拉形式显示出来,展示形式为模态形式(暂停执行,直到单击界面中的按钮关闭下拉窗体程序才继续执行)。而EditValue方法是在单击属性窗口中属性旁边的下拉按钮时触发。

string strReturn = form.strReturnValue;

return (string)value;

以上代码是获取下拉窗体中当前选择的数据(单击“肯定”按钮时把值暂存到form.str ReturnValue变量中)并返回,系统会自动把返回值赋给当前属性。

最后定义主控件代码类,代码以下;

/// <summary>

/// 得到本书更多内容,请看:

/// http://blog.csdn.net/ChengKing/archive/2008/08/18/2792440.aspx

/// </summary>

 

[DefaultProperty("SelectFood")]

[ToolboxData("<{0}:CustomeDropDownEditorControl runat=server></{0}: CustomeDropDownEditorControl>")]

public class CustomeDropDownEditorControl : WebControl

{

    [Bindable(true)]

    [Category("类别")]

    [DefaultValue("")]

    [Localizable(true)]

    [Editor(typeof(CategoryDropDownEditor), typeof(System.Drawing.Design. UITypeEditor))]

    [Description("选择食品类别")]

    public string SelectFood

    {

        get

        {

            String s = (String)ViewState["SelectFood"];

            return ((s == null) ? String.Empty : s);

        }

 

        set

        {

            ViewState["SelectFood"] = value;

        }

    }

    //… …

}

u  主控件中仅包括一个SelectFood属性。这里仅须要说明的是它的设计时元数据代码段,以下:

[Editor(typeof(CategoryDropDownEditor), typeof(System.Drawing.Design.UITypeEditor))]

以上代码指定编辑器为咱们上面定义的CategoryDropDownEditor编辑器。

最后,编译控件,在属性窗口中便可看到咱们定义的属性,如图4-25所示。

 

4-25  属性窗口

 

4.5.2.4  定制计算器属性编辑器

本节没有讲解新的控件开发知识,而是利用前面所讲解的知识编写了一个有用的自定义属性编辑器—计算器属性编辑器,如图4-26所示。

 

4-26  计算器属性编辑器

这个属性编辑器比较简单,下面就简要地说一下它的实现过程。首先看一下计算器面板Form的实现代码:

 

  1. /// <summary>
  2. /// 得到本书更多内容,请看:
  3. /// http://blog.csdn.net/ChengKing/archive/2008/08/18/2792440.aspx
  4. /// </summary> 
  5. public class FormKeyBoard : System.Windows.Forms.Form
  6. {
  7.     private System.Windows.Forms.Label label2;
  8.     private System.Windows.Forms.TextBox expressBox;
  9.     private System.ComponentModel.Container components = null;
  10.     public string strReturnValue = "";
  11.     
  12.     //定义存放运算符(包括:'+','-',...,'sin',...,'arcsin',...,'(',...等)及其特
  13.     //性的数据结构
  14.     public struct opTable   //定义存放运算符及其优先级和单双目的结构
  15.     {
  16.         public string op;   //用于存放运算符,op为operator的简写
  17.         public int code;    //用于存放运算符的优先级
  18.         public char grade;  //用于判断存放的运算符是单目仍是双目
  19.     }
  20.     
  21.     //用于存放制定好的运算符及其特性(优先级和单双目)的运算符表,其初始化在方
  22.     //法Initialize()中
  23.     public opTable[] opchTbl=new opTable[19];  
  24.     public opTable[] operateStack=new opTable[30];//用于存放从键盘扫描的运算符的栈
  25.     
  26.     //定义优先级列表1,2,3,4,5,6,7,8,9,
  27.     //数组中元素依次为: //"sin","cos","tan","cot","arcsin","arccos","arctan", "sec","csc","ln","^","*","/","+","-","(",")",""   的栈外(由于有的运算符是从右向左计算,有的是从左往右计算,用内外优先级能够限制其执行顺序)优先级
  28. public int[]osp=new int[19]{6,6,6,6,6,6,6,6,6,6,6,5,3,3,2,2,7,0,1};  
  29.     //数组中元素依次为: //"sin","cos","tan","cot","arcsin","arccos","arctan", "sec","csc","ln","^","*","/","+","-","(" ,"end" 的栈内(由于有的运算符是从右向左计算,有的是从左往右计算,用内外优先级能够限制其执行顺序)优先级
  30. public int[]isp=new int[18]{5,5,5,5,5,5,5,5,5,5,5,4,3,3,2,2,1,1};      
  31.         
  32.     //定义存放从键盘扫描的数据的栈
  33.     public double[]dataStack=new double[30]{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0};
  34.     //定义表态指针
  35.     public int opTop=-1;  //指向存放(从键盘扫描的)运算符栈的指针
  36.     public int dataTop=-1;//指向存放(从键盘扫描的)数据栈指针
  37.     
  38.     //定义存放从键盘输入的起始字符串
  39.     public string startString;
  40.     public int startTop=0;
  41.     public double variableX=0;
  42.     public double variableY=0;
  43.     const double PI=3.1415926;
  44.     int number=1;
  45.     public int startTopMoveCount=0;  
  46.     private System.Windows.Forms.Button button1;
  47.     private System.Windows.Forms.Button button2;
  48.     private System.Windows.Forms.Button button3;
  49.     private System.Windows.Forms.Button button4;
  50.     private System.Windows.Forms.Button button5;
  51.     private System.Windows.Forms.Button button6;
  52.     private System.Windows.Forms.Button button7;
  53.     private System.Windows.Forms.Button button8;
  54.     private System.Windows.Forms.Button button9;
  55.     private System.Windows.Forms.Button button10;
  56.     private System.Windows.Forms.Button button11;
  57.     private System.Windows.Forms.Button button12;
  58.     private System.Windows.Forms.Button button13;
  59.     private System.Windows.Forms.Button button14;
  60.     private System.Windows.Forms.Button button15;
  61.     private System.Windows.Forms.Button button16;
  62.     private System.Windows.Forms.Button button17;
  63.     private System.Windows.Forms.Button button18;
  64.     private System.Windows.Forms.Button button19;
  65.     private System.Windows.Forms.Button button20;
  66.     private System.Windows.Forms.Button button21;
  67.     private System.Windows.Forms.Button button22;
  68.     private System.Windows.Forms.Button button23;
  69.     private System.Windows.Forms.Button button24;
  70.     private System.Windows.Forms.Button button25;
  71.     private System.Windows.Forms.Button button26;
  72.     private System.Windows.Forms.Button button27;
  73.     private System.Windows.Forms.Button button28;
  74.     private System.Windows.Forms.Button button29;
  75.     private System.Windows.Forms.Button button30;
  76.     private System.Windows.Forms.Button button31;
  77.     private System.Windows.Forms.Button button32;
  78.     private System.Windows.Forms.Label label1;
  79.     private System.Windows.Forms.TextBox endbox;
  80.     private System.Windows.Forms.Button button33;
  81.     private Button btnClear;
  82.     private Button button34;
  83.     private System.Windows.Forms.Button btnReturn;   
  84.     
  85.     
  86.     
  87.     #region Windows Form Designer generated code
  88.     public FormKeyBoard()
  89.     {
  90.         InitializeComponent();      
  91.     }
  92.     protected override void Dispose( bool disposing )
  93.     {
  94.         if( disposing )
  95.         {
  96.             if (components != null
  97.             {
  98.                 components.Dispose();
  99.             }
  100.         }
  101.         base.Dispose( disposing );
  102.     }
  103.     #endregion 
  104.     #region Windows Form Designer generated code
  105.     /// <summary>
  106.     /// 设计器支持所需的方法- 不要使用代码编辑器修改
  107.     /// 此方法的内容。
  108.     /// </summary>
  109.     private void InitializeComponent()
  110.     {
  111.     //本方法主要完成控件的建立,初始化,注册事件等逻辑。完整代码在本书随书光盘中
  112.     }
  113.     #endregion
  114.     private void Form1_Load(object sender, System.EventArgs e)
  115.     {
  116.     }
  117.     //制定运算符及其特性(优先级和单双目)的运算符表
  118.     public void InitializeOpchTblStack()  
  119.     {
  120.         opchTbl[0].op="sin"; opchTbl[0].code=1; opchTbl[0].grade='s';
  121.         opchTbl[1].op="cos"; opchTbl[1].code=2; opchTbl[1].grade='s'
  122.         opchTbl[2].op="tan"; opchTbl[2].code=3; opchTbl[2].grade='s'
  123.         opchTbl[3].op="cot"; opchTbl[3].code=4; opchTbl[3].grade='s';
  124.         opchTbl[4].op="arcsin"; opchTbl[4].code=5; opchTbl[4].grade='s';
  125.         opchTbl[5].op="arccos"; opchTbl[5].code=6; opchTbl[5].grade='s';
  126.         opchTbl[6].op="arctan"; opchTbl[6].code=7; opchTbl[6].grade='s';
  127.         opchTbl[7].op="arccot"; opchTbl[7].code=8; opchTbl[7].grade='s';
  128.         opchTbl[8].op="sec"; opchTbl[8].code=9; opchTbl[8].grade='s';
  129.         opchTbl[9].op="csc"; opchTbl[9].code=10; opchTbl[9].grade='s';
  130.         opchTbl[10].op="ln"; opchTbl[10].code=11; opchTbl[10].grade='s';
  131.         opchTbl[11].op="^"; opchTbl[11].code=12; opchTbl[11].grade='d';
  132.         opchTbl[12].op="*"; opchTbl[12].code=13; opchTbl[12].grade='d';
  133.         opchTbl[13].op="/"; opchTbl[13].code=14; opchTbl[13].grade='d';
  134.         opchTbl[14].op="+"; opchTbl[14].code=15; opchTbl[14].grade='d';
  135.         opchTbl[15].op="-"; opchTbl[15].code=16; opchTbl[15].grade='d';
  136.         opchTbl[16].op="("; opchTbl[16].code=17; opchTbl[16].grade='d';
  137.         opchTbl[17].op=")"; opchTbl[17].code=18; opchTbl[17].grade='d';
  138.         opchTbl[18].op=" "; opchTbl[18].code=19; opchTbl[18].grade='d';
  139.         startString=expressBox.Text;
  140.     }
  141.     public void CreterionFaction()
  142.     {
  143.         //如下代码消去待扫描字符串中的全部空格字符
  144.         for(int i=0;i<startString.Length;i++)
  145.             if(startString[i].Equals(' '))
  146.             {
  147.                 startString=startString.Remove(i,1);
  148.                 i--;
  149.             }
  150.         //如下代码使待扫描字符串的单目('+'和'-')变为双目
  151.         if(startString.Length!=0)
  152.             if(startString[0]=='+'||startString[0]=='-')
  153.             {
  154.                 startString=startString.Insert(0,"0");
  155.             }
  156.         for(int i=0;i<startString.Length-1;i++)
  157.         {
  158.             if((startString[i]=='(')&&(startString[i+1]=='-'))
  159.                 startString=startString.Insert(i+1,"0");
  160.         }
  161.         startString=startString.Insert(startString.Length,")");
  162.         //将待扫描字符串转化为小写字母
  163.         startString=startString.ToLower();
  164.     }
  165.     public bool CheckParentthese() //检查括号是否匹配
  166.     {
  167.         int number=0;
  168.         for(int i=0;i<startString.Length-1;i++)
  169.         {
  170.             if(i=='(') number++;
  171.             if(i==')') number--;
  172.             if(number<0) return false;
  173.         }
  174.         if(number!=0)  
  175.         {
  176.             return false;
  177.         }
  178.         return true;
  179.     }
  180.     //给运算表达式分块(三角函数、算术运算符等),再根据其返回值来检验其属于哪类错误
  181.     public int CheckFollowCorrect()    
  182.     {
  183.         string str,oldString="",newString="";
  184.         int dataCount=0,characterCount=0;
  185.         if(startString.Equals(")"))       
  186.             return 0;         //输入字符串为空返回值
  187. if((startString[0]=='*')||(startString[0]=='/')||(startString[0]=='^')||
  188.     (startString[0]==')'))
  189.             return 11;        //首字符输入错误返回值
  190. for(int i=0;i<startString.Length;i++)
  191. {
  192.     if((oldString.Equals("三角函数"))&&(newString.Equals("右括号")))
  193.         return 2;     //三角函数直接接右括号错误返回值
  194.     if((oldString.Equals("左括号"))&&(newString.Equals("算术运算符")))
  195.         return 3;     //左括号直接接算术运算符错误返回值
  196.     if((oldString.Equals("数字序列"))&&(newString.Equals("三角函数")))
  197.         return 4;     //数字序列后直接接三角函数错误返回值
  198.     if((oldString.Equals("数字序列"))&&(newString.Equals("左括号")))
  199.         return 5;     //数字序列后直接接左括号错误返回值
  200.     if((oldString.Equals("算术运算符"))&&(newString.Equals("右括号")))
  201.         return 6;     //算术运算符后直接接右括号错误返回值
  202.     if((oldString.Equals("右括号"))&&(newString.Equals("左括号")))
  203.         return 7;     //右括号直接接左括号错误返回值
  204.     if((oldString.Equals("右括号"))&&(newString.Equals("三角函数")))
  205.         return 8;     //右括号直接接三角函数错误返回值
  206.     if((oldString.Equals("数字序列"))&&(newString.Equals("数字序列")))
  207.         return 9;     //数字序列后直接接'pi'/'e'或'pi'/'e'直接接数字序列错误返回值
  208.     if((oldString.Equals("算术运算符"))&&(newString.Equals("算术运算符")))
  209.         return 10;    //算术运算符后直接接算术运算符错误返回值
  210.     oldString=newString;
  211.     if(i<startString.Length-5&&startString.Length>=6)
  212.     {
  213.         str=startString.Substring(i,6); 
  214. if((str.CompareTo("arcsin")==0)||(str.CompareTo("arccos")==0)||(str.Compar
  215.     eTo("arctan")==0)||(str.CompareTo("arccot")==0))
  216.     {
  217.         newString="三角函数";
  218.         i+=5; characterCount++;
  219.         continue;
  220.     }
  221. }
  222. if(i<startString.Length-2&&startString.Length>=3)
  223. {
  224.     str=startString.Substring(i,3);
  225. if((str.CompareTo("sin")==0)||(str.CompareTo("cos")==0)||(str.CompareTo("tan")==0)||(str.CompareTo("cot")==0)||(str.CompareTo("sec")==0)||(str.CompareTo("csc")==0))
  226.     {
  227.         newString="三角函数";
  228.         i+=2; characterCount++;
  229.         continue;
  230.     }
  231. }
  232. if(i<(startString.Length-1)&&(startString.Length)>=2)
  233. {
  234.     str=startString.Substring(i,2);
  235.     if(str.CompareTo("ln")==0)
  236.     {
  237.         newString="三角函数";
  238.         i+=1; characterCount++;
  239.         continue;
  240.     }
  241.     if(str.CompareTo("pi")==0)
  242.     {
  243.         newString="数字序列";
  244.         i+=1;dataCount++;
  245.         continue;
  246.     }
  247.     }
  248.     str=startString.Substring(i,1);
  249. if(str.Equals("^")||str.Equals("*")||str.Equals("/")||str.Equals("+")||str.Equals("-"))
  250.     {
  251.         newString="算术运算符";
  252.         characterCount++;
  253.         continue;
  254.     }
  255.     if(str.Equals("e"))
  256.     {
  257.         newString="数字序列";
  258.         dataCount++;
  259.         continue;
  260.     }
  261.     if(str.Equals("("))
  262.     {
  263.         newString="左括号";
  264.         characterCount++;
  265.         continue;
  266.     }
  267.     if(str.Equals(")"))
  268.     {
  269.         newString="右括号";
  270.         characterCount++;
  271.         continue;
  272.     }
  273.     if(Char.IsDigit(startString[i]))
  274.     {
  275.         while(Char.IsDigit(startString[i]))
  276.     {
  277.         i++;                                          
  278.     }
  279. if(startString[i]=='.'&&(!Char.IsDigit(startString[i+1]))&&(i+1)!=startString.Length)
  280.         return 13;
  281.     if(startString[i]=='.')
  282.     {
  283.         i++;
  284.     }                    
  285.     while(Char.IsDigit(startString[i]))
  286.     {
  287.         i++;  
  288.     }
  289.     newString="数字序列";
  290.     i--; dataCount++;
  291.     continue;
  292.     }
  293.     return 1;         //非法字符
  294. if((dataCount==0&&characterCount!=0)||(startString[0]=='0'&&dataCount==1&
  295.    characterCount>1&&startString.Length!=2))
  296.             return 12;
  297.         return 100;
  298.     }
  299.     public int IsCharacterOrData(ref double num)
  300.     {
  301.         string str="";
  302.         startTop+=startTopMoveCount; startTopMoveCount=0;
  303.         int i=startTop;
  304.         if(i<startString.Length-5&&startString.Length>=6)
  305.         {
  306.             str=startString.Substring(i,6); 
  307.             for(int j=4;j<=7;j++)
  308.                 if(str.Equals(opchTbl[j].op))
  309.                 {
  310.                     startTopMoveCount=6;
  311.                     return opchTbl[j].code;
  312.                 }
  313.         }
  314.         if(i<startString.Length-2&&startString.Length>=3)
  315.         {                 
  316.             str=startString.Substring(i,3);                 
  317.             for(int j=0;j<10;j++)
  318.                 if(str.CompareTo(opchTbl[j].op)==0)
  319.                 {
  320.                     startTopMoveCount=3;
  321.                     return opchTbl[j].code;
  322.                 }
  323.         }
  324.         if(i<(startString.Length-1)&&(startString.Length)>=2)
  325.         {
  326.             str=startString.Substring(i,2);
  327.             if(str.CompareTo("ln")==0)
  328.             {
  329.                 startTopMoveCount=2;
  330.                 return 11;
  331.             }
  332.             if(str.CompareTo("pi")==0)
  333.             {
  334.                 startTopMoveCount=2;
  335.                 num=Math.PI;
  336.                 return 100;
  337.             }
  338.         }
  339.         //如下开始确认一个字符是属于什么值类型
  340.         if(i<startString.Length)
  341.         {
  342.             str=startString.Substring(i,1);
  343.             for(int j=11;j<19;j++)
  344.             {
  345.                 if(str.Equals(opchTbl[j].op))
  346.                 {startTopMoveCount=1;return opchTbl[j].code;}                
  347.             }
  348.             if(str.CompareTo("e")==0)
  349.             {
  350.                 startTopMoveCount=1; num=Math.E;
  351.                 return 100;
  352.             }  
  353.             if(Char.IsDigit(startString[i]))
  354.             {
  355.                 double temp=0,M=10; int j=i;
  356.                 while(Char.IsDigit(startString[j]))
  357.                 {
  358.                     temp=M*temp+Char.GetNumericValue(startString[j]);
  359.                     startTop++;
  360.                     j++;                      
  361.                 }
  362.                 if(startString[j]=='.')
  363.                 {
  364.                     j++;startTop++;                       
  365.                 }
  366.                 while(Char.IsDigit(startString[j]))
  367.                 {
  368.                     temp+=1.0/M*Char.GetNumericValue(startString[j]);
  369.                     M/=10;j++;    
  370.                     startTop++;
  371.                 }
  372.                 startTopMoveCount=0;
  373.                 num=temp;
  374.                 return 100;
  375.             }
  376.         }
  377.         return -1;
  378.     }
  379.     public double DoubleCount(string opString,double data1,double data2)
  380.     {   //双目运算
  381.         if(opString.CompareTo("+")==0) return (data1+data2);
  382.         if(opString.CompareTo("-")==0) return (data1-data2);
  383.         if(opString.CompareTo("*")==0) return (data1*data2);
  384.         if(opString.CompareTo("/")==0) return (data1/data2);
  385.         if(opString.CompareTo("^")==0) 
  386.         {
  387.             double end=data1;
  388.             for(int i=0;i<data2-1;i++)
  389.                 end*=data1;
  390.             return (end);
  391.         }            
  392.         return Double.MaxValue;    //定义域不对,返回
  393.     }
  394.     public double DoubleCount(string opString,double data1)
  395.     {   //单目运算
  396.         if(opString.CompareTo("sin")==0) return Math.Sin(data1);
  397.         if(opString.CompareTo("cos")==0) return Math.Cos(data1);
  398.         if(opString.CompareTo("tan")==0) return Math.Tan(data1);
  399.         if(opString.CompareTo("cot")==0) return (1/(Math.Tan(data1)));
  400.         if(opString.CompareTo("arcsin")==0)
  401.         if(-1<=data1&&data1<=1)    return Math.Asin(data1);
  402.     
  403.         if(opString.CompareTo("arccos")==0) 
  404.             if(-1<=data1&&data1<=1)      return Math.Acos(data1);
  405.         
  406.         if(opString.CompareTo("arctan")==0)
  407.             if(-Math.PI/2<=data1&&data1<=Math.PI/2)return Math.Atan(data1);
  408.         if(opString.CompareTo("arccot")==0)
  409.             if(-Math.PI/2<=data1&&data1<=Math.PI/2)return (-Math.Atan(data1));
  410.         if(opString.CompareTo("sec")==0) return (1/(Math.Cos(data1)));
  411.         if(opString.CompareTo("csc")==0) return (1/(Math.Sin(data1)));
  412.         if(data1>0) if(opString.CompareTo("ln")==0) return  Math.Log(data1);
  413.         return Double.MaxValue;   //定义域不对
  414.     }
  415.     public bool CountValueY(ref double tempY)  //此方法功能为求解
  416.     {
  417.         int type=-1;       //存放正在扫描的字符串是为数字类型仍是单双目运算符
  418.         double num=0;      //若是是数据,则返回数据的值
  419.         //进栈底结束符"end"
  420.         opTop++;
  421.         operateStack[opTop].op="end"; operateStack[opTop].code=18; 
  422.         operateStack[opTop].grade=' ';
  423.         while(startTop<=startString.Length-1)
  424.         {
  425.         start:
  426.             type=IsCharacterOrData(ref num);  //调用判断返回值类型函数
  427.             if(type==-1){return false;}                
  428.             if(type==100) 
  429.             {                
  430.                 dataTop=dataTop+1;
  431.                 dataStack[dataTop]=num;                                                            
  432.             }    
  433.             else
  434.             {   
  435.                 if(osp[type-1]>isp[operateStack[opTop].code-1])   //操做符进栈
  436.                 {
  437.                     opTop++;
  438.                     operateStack[opTop].op=opchTbl[type-1].op; 
  439.                     operateStack[opTop].code=opchTbl[type-1].code; 
  440.                     operateStack[opTop].grade=opchTbl[type-1].grade; 
  441.                 }
  442.                 else
  443.     {
  444.         //弹出操做符跟数据计算,并存入数据
  445.         while(osp[type-1]<=isp[operateStack[opTop].code-1])  
  446.         {    
  447.             //当遇到"end"结束符表示已经得到结果
  448.             if(operateStack[opTop].op.CompareTo("end")==0) 
  449.             {
  450.                 if(dataTop==0)
  451.                 {
  452.                     tempY=dataStack[dataTop];  startTop=0; startTopMoveCount=0; 
  453.                     opTop=-1; dataTop=-1;
  454.                     return true;
  455.                 }
  456.                 else return false;//运算符和数据的个数不匹配形成的错误
  457.             }
  458.             if(operateStack[opTop].op.CompareTo("(")==0)  //若是要弹出操做数为
  459.                                                                    //'( ',则消去左括号
  460.             {
  461.                 opTop--; goto start;
  462.             }  
  463.             //弹出操做码和一个或两个数据计算,并将计算结果存入数据栈
  464.             double data1,data2; opTable operate;
  465.             if(dataTop>=0)    data2=dataStack[dataTop];
  466.             else return false;
  467.             operate.op=operateStack[opTop].op; operate.code=operateStack
  468.             [opTop].code; operate.grade=operateStack[opTop].grade;
  469.             opTop--;  //处理一次,指针必须仅且只能下移一个单位
  470.             if(operate.grade=='d')
  471.                 {
  472.                 if(dataTop-1>=0)    data1=dataStack[dataTop-1];
  473.                 else return false;
  474.                 double tempValue=DoubleCount(operate.op,data1,data2);
  475.                 if(tempValue!=Double.MaxValue)dataStack[--dataTop]=tempValue;
  476.                 else return false;
  477.             }
  478.             if(operate.grade=='s')
  479.             {
  480.                 double tempValue=DoubleCount(operate.op,data2);
  481.                 if(tempValue!=Double.MaxValue)
  482.                     dataStack[dataTop]=tempValue;
  483.                     else return false;
  484.                 }
  485.             }                                        
  486.             //若是当前栈外操做符比栈顶的操做符优先级别高,则栈外操做符进栈
  487.                 opTop++;
  488.                 operateStack[opTop].op=opchTbl[type-1].op; 
  489.                 operateStack[opTop].code=opchTbl[type-1].code; 
  490.                 operateStack[opTop].grade=opchTbl[type-1].grade;
  491.             }
  492.         }
  493.     }
  494.     return false
  495. }
  496. public void StartExcute()
  497. {
  498.     InitializeOpchTblStack();
  499.     CreterionFaction();
  500.     if(CheckParentthese()==false)
  501.     {
  502.         MessageBox.Show("括号不匹配,请从新输入!!!","错误",MessageBoxButtons.OK, 
  503.              MessageBoxIcon.Error);
  504.         return
  505.     }
  506.     switch(CheckFollowCorrect())
  507.     {
  508.         case 0: MessageBox.Show("表达式为空,请先输入表达式!!!","错误"
  509.                   MessageBoxButtons.OK,MessageBoxIcon.Warning); 
  510.         return;
  511.         case 1:  MessageBox.Show("表达式中有非法字符!!!","错误"
  512.                   MessageBoxButtons.OK,MessageBoxIcon.Error); 
  513.         return;
  514.         case 2:  MessageBox.Show("三角函数运算符与) 之间应输入数据或其余表达式!!!","
  515.                   错误",MessageBoxButtons.OK,MessageBoxIcon.Error);
  516.          return;
  517.         case 3:  MessageBox.Show("' (  ' 与算术运算符之间应输入数据或其余表达
  518.                   式!!!","错误",MessageBoxButtons.OK,MessageBoxIcon.Error);
  519.         return;
  520.         case 4:  MessageBox.Show("数字数列与三角函数之间应输入算术运算符或其余表达
  521.                   式!!!","错误",MessageBoxButtons.OK,MessageBoxIcon.Error);
  522.         return;
  523.         case 5:  MessageBox.Show("数字数列与 ' (  '  之间应输入算术运算符或其余表达
  524.                   式!!!","错误",MessageBoxButtons.OK,MessageBoxIcon.Error);
  525.     return;
  526.         case 6:  MessageBox.Show("算术运算符与右括号之间应输入数据或其余表达式!!!","
  527.                   错误",MessageBoxButtons.OK,MessageBoxIcon.Error);
  528.         return;
  529.         case 7:  MessageBox.Show("'  )  ' 与'  (  ' 之间应输入算术运算符或其余表达
  530.                   式!!!","错误",MessageBoxButtons.OK,MessageBoxIcon.Error);
  531.         return;
  532.         case 8:  MessageBox.Show("'   )   ' 与三角函数之间应输入算术运算符或其余表达
  533.              式!!!","错误",MessageBoxButtons.OK,MessageBoxIcon.Error);
  534.         return;
  535.         case 9:  MessageBox.Show("常量'  PI  '  或 '  E  '  或 '  X  '  与数字数据
  536.                   之间应输入算术运算符或其余表达式!!!","错误", 
  537.                   MessageBoxButtons.OK,MessageBoxIcon.Error);
  538.         return;
  539.         case 10: MessageBox.Show("算术运算符与算术运算符之间应输入数据或其余表达
  540.                   式!!!","错误",MessageBoxButtons.OK,MessageBoxIcon.Error); 
  541.         return;
  542.         case 11: MessageBox.Show("表达式头部不能为' + ',' - ',' * ',' / '' ^ ',' )'
  543.                  !!!","错误",MessageBoxButtons.OK,MessageBoxIcon.Error);
  544.         return;    
  545.         case 12: MessageBox.Show("仅有运算符号没有数字数据或数据缺乏而没法计算,请输入数
  546.                  字数据!!!","错误",MessageBoxButtons.OK,MessageBoxIcon.Error);
  547.         return;
  548.         case 13: MessageBox.Show("小数点后面缺乏小数部分,请输入小数部分!!!","错误",
  549.                   MessageBoxButtons.OK,MessageBoxIcon.Error);
  550.         return;
  551.     }
  552.     double tempY=0;
  553.     switch(CountValueY(ref tempY))
  554.     {                
  555.         case false:MessageBox.Show("输入的表达式不正确或反三角函数定义域在其定义域范围
  556.                     以外!!!","错误",MessageBoxButtons.OK,MessageBoxIcon.Error);
  557.         return;
  558.     }            
  559.     endbox.Text=tempY.ToString();//依次存档计算结果
  560.     number++;            
  561. }
  562. private void button30_Click(object sender, System.EventArgs e)
  563. {
  564.     StartExcute();
  565. }
  566. private void button10_Click(object sender, System.EventArgs e)
  567. {
  568.     expressBox.SelectedText=null;
  569.     expressBox.Text=expressBox.Text.Insert(expressBox.SelectionStart, button10.Text);            
  570.     expressBox.SelectionStart=expressBox.TextLength;
  571. }
  572. private void button11_Click(object sender, System.EventArgs e)
  573. {
  574.     expressBox.SelectedText=null;
  575.     expressBox.Text=expressBox.Text.Insert(expressBox.SelectionStart,".");            
  576.     expressBox.SelectionStart=expressBox.TextLength;
  577. }
  578. private void button27_Click(object sender, System.EventArgs e)
  579. {
  580.     expressBox.SelectedText=null;
  581.     expressBox.Text=expressBox.Text.Insert(expressBox.SelectionStart,"^");            
  582.     expressBox.SelectionStart=expressBox.TextLength;
  583. }
  584. private void button1_Click_1(object sender, System.EventArgs e)
  585. {        
  586.     expressBox.SelectedText=null;
  587.     expressBox.Text=expressBox.Text.Insert(expressBox.SelectionStart, button1.Text);
  588.     expressBox.SelectionStart=expressBox.TextLength;
  589. }
  590. private void button4_Click(object sender, System.EventArgs e)
  591. {
  592.     expressBox.SelectedText=null;
  593.     expressBox.Text=expressBox.Text.Insert(expressBox.SelectionStart, button4.Text);
  594.     expressBox.SelectionStart=expressBox.TextLength;
  595. }
  596. private void button3_Click(object sender, System.EventArgs e)
  597. {
  598.     expressBox.SelectedText=null;
  599.     expressBox.Text=expressBox.Text.Insert(expressBox.SelectionStart, button3.Text);
  600.     expressBox.SelectionStart=expressBox.TextLength;
  601. }
  602. private void button2_Click_1(object sender, System.EventArgs e)
  603. {
  604.     expressBox.SelectedText=null;
  605.     expressBox.Text=expressBox.Text.Insert(expressBox.SelectionStart, button2.Text);
  606.     expressBox.SelectionStart=expressBox.TextLength;
  607. }
  608. private void button14_Click(object sender, System.EventArgs e)
  609. {
  610.     expressBox.SelectedText=null;
  611.     expressBox.Text=expressBox.Text.Insert(expressBox.SelectionStart, button14.Text);
  612.     expressBox.SelectionStart=expressBox.TextLength;
  613. }
  614. private void button15_Click(object sender, System.EventArgs e)
  615. {
  616.     expressBox.SelectedText=null;
  617.     expressBox.Text=expressBox.Text.Insert(expressBox.SelectionStart, button15.Text);
  618.     expressBox.SelectionStart=expressBox.TextLength;
  619. }
  620. private void button5_Click(object sender, System.EventArgs e)
  621. {            
  622.     expressBox.SelectedText=null;
  623.     expressBox.Text=expressBox.Text.Insert(expressBox.SelectionStart, button5.Text);
  624.     expressBox.SelectionStart=expressBox.TextLength;
  625. }
  626. private void button6_Click(object sender, System.EventArgs e)
  627. {
  628.     expressBox.SelectedText=null;
  629.     expressBox.Text=expressBox.Text.Insert(expressBox.SelectionStart, button6.Text);
  630.     expressBox.SelectionStart=expressBox.TextLength;
  631. }
  632. private void button9_Click(object sender, System.EventArgs e)
  633. {
  634.     expressBox.SelectedText=null;
  635.     expressBox.Text=expressBox.Text.Insert(expressBox.SelectionStart, button9.Text);
  636.     expressBox.SelectionStart=expressBox.TextLength;
  637. }
  638. private void button8_Click(object sender, System.EventArgs e)
  639. {
  640.     expressBox.SelectedText=null;
  641.     expressBox.Text=expressBox.Text.Insert(expressBox.SelectionStart, button8.Text);
  642.     expressBox.SelectionStart=expressBox.TextLength;
  643. }
  644. private void button7_Click(object sender, System.EventArgs e)
  645. {
  646.     expressBox.SelectedText=null;
  647.     expressBox.Text=expressBox.Text.Insert(expressBox.SelectionStart, button7.Text);
  648.     expressBox.SelectionStart=expressBox.TextLength;
  649. }
  650. private void button12_Click(object sender, System.EventArgs e)
  651. {
  652.     expressBox.SelectedText=null;
  653.     expressBox.Text=expressBox.Text.Insert(expressBox.SelectionStart, button12.Text);
  654.     expressBox.SelectionStart=expressBox.TextLength;
  655. }
  656. private void button13_Click(object sender, System.EventArgs e)
  657. {
  658.     expressBox.SelectedText=null;
  659.     expressBox.Text=expressBox.Text.Insert(expressBox.SelectionStart, button13.Text);
  660.     expressBox.SelectionStart=expressBox.TextLength;
  661. }
  662. private void button29_Click(object sender, System.EventArgs e)
  663. {            
  664.     expressBox.SelectedText=null;
  665.     expressBox.Text=expressBox.Text.Insert(expressBox.SelectionStart, button29.Text);
  666.     expressBox.SelectionStart=expressBox.TextLength;
  667. }
  668. private void button28_Click(object sender, System.EventArgs e)
  669. {
  670.     expressBox.SelectedText=null;
  671.     expressBox.Text=expressBox.Text.Insert(expressBox.SelectionStart, button28.Text);
  672.     expressBox.SelectionStart=expressBox.TextLength;
  673. }
  674. private void button16_Click(object sender, System.EventArgs e)
  675. {
  676.     expressBox.SelectedText=null;
  677.     expressBox.Text=expressBox.Text.Insert(expressBox.SelectionStart, button16.Text);
  678.     expressBox.SelectionStart=expressBox.TextLength;
  679. }
  680. private void button20_Click(object sender, System.EventArgs e)
  681. {
  682.     expressBox.SelectedText=null;
  683.     expressBox.Text=expressBox.Text.Insert(expressBox.SelectionStart, button20.Text);            
  684.     expressBox.SelectionStart=expressBox.TextLength;
  685. }
  686. private void button17_Click(object sender, System.EventArgs e)
  687. {
  688.     expressBox.SelectedText=null;
  689.     expressBox.Text=expressBox.Text.Insert(expressBox.SelectionStart, button17.Text);
  690.     expressBox.SelectionStart=expressBox.TextLength;
  691. }
  692. private void button21_Click(object sender, System.EventArgs e)
  693. {
  694.     expressBox.SelectedText=null;
  695.     expressBox.Text=expressBox.Text.Insert(expressBox.SelectionStart, button21.Text);            
  696.     expressBox.SelectionStart=expressBox.TextLength;
  697. }
  698. private void button24_Click(object sender, System.EventArgs e)
  699. {
  700.     expressBox.SelectedText=null;
  701.     expressBox.Text=expressBox.Text.Insert(expressBox.SelectionStart, button24.Text);
  702.     expressBox.SelectionStart=expressBox.TextLength;
  703. }
  704. private void button18_Click(object sender, System.EventArgs e)
  705. {
  706.     expressBox.SelectedText=null;
  707.     expressBox.Text=expressBox.Text.Insert(expressBox.SelectionStart, button18.Text);            
  708.     expressBox.SelectionStart=expressBox.TextLength;
  709. }
  710. private void button22_Click(object sender, System.EventArgs e)
  711. {
  712.     expressBox.SelectedText=null;
  713.     expressBox.Text=expressBox.Text.Insert(expressBox.SelectionStart, button22.Text);
  714.     expressBox.SelectionStart=expressBox.TextLength;
  715. }
  716. private void button25_Click(object sender, System.EventArgs e)
  717. {
  718.     expressBox.SelectedText=null;
  719.     expressBox.Text=expressBox.Text.Insert(expressBox.SelectionStart, button25.Text);
  720.     expressBox.SelectionStart=expressBox.TextLength;
  721. }
  722. private void button19_Click(object sender, System.EventArgs e)
  723. {
  724.     expressBox.SelectedText=null;
  725.     expressBox.Text=expressBox.Text.Insert(expressBox.SelectionStart, button19.Text);            
  726.     expressBox.SelectionStart=expressBox.TextLength;
  727. }
  728. private void button23_Click(object sender, System.EventArgs e)
  729. {
  730.     expressBox.SelectedText=null;
  731.     expressBox.Text=expressBox.Text.Insert(expressBox.SelectionStart, button23.Text);
  732.     expressBox.SelectionStart=expressBox.TextLength;
  733. }
  734. private void button26_Click(object sender, System.EventArgs e)
  735. {
  736.     expressBox.SelectedText=null;
  737.     expressBox.Text=expressBox.Text.Insert(expressBox.SelectionStart, button26.Text);
  738.     expressBox.SelectionStart=expressBox.TextLength;
  739. }
  740. private void button31_Click(object sender, System.EventArgs e)
  741. {
  742.     if(expressBox.Text.Length>0)
  743.         expressBox.Text=expressBox.Text.Remove(expressBox.Text.Length-1,1);
  744. }
  745. private void button32_Click(object sender, System.EventArgs e)
  746. {
  747.     expressBox.Text="";
  748.     endbox.Text="0.000000";
  749. }
  750. private void button33_Click(object sender, System.EventArgs e)
  751. {
  752.     expressBox.SelectedText=null;
  753.     expressBox.Text=expressBox.Text.Insert(expressBox.SelectionStart,"PI");
  754.     expressBox.SelectionStart=expressBox.TextLength;
  755. }
  756. private void button35_Click(object sender, System.EventArgs e)
  757. {            
  758.     strReturnValue = endbox.Text.Trim();
  759. }
  760. private void btnClear_Click(object sender, EventArgs e)
  761. {
  762.     expressBox.Text = "";
  763. }
  764. private void button34_Click(object sender, EventArgs e)
  765. {
  766.     StartExcute();
  767. }
  768. }

上面代码为一个WinForm窗体主要实现展现一个计算器功能此计算器能够一次性计算多个操做项的值例如:y = 3 + 64 * (2 + 3^5) + sinPI的值,这是它与其余计算器的不一样之处好比Windows自带的计算器一次只能计算两个操做数这里能够支持您任意输入多个操做数的表达式最后一块计算出结果支持:sincostancotarcsinarccosarctanseccscln^*/+-()运算符并用括号区分优先级另外此计算器的实现算法也不错采用经典的Stack编译算法感兴趣的读者能够研究一下。

而后定义一个编辑器,其实全部这些的编辑器功能至关于一个“桥接器”,使属性与自定义Form窗体关联起来。代码以下:

 

  1. /// <summary>
  2. /// 得到本书更多内容,请看:
  3. /// http://blog.csdn.net/ChengKing/archive/2008/08/18/2792440.aspx
  4. /// </summary> 
  5. public class CalculatorSelectEditor : System.Drawing.Design.UITypeEditor
  6. {
  7.     public CalculatorSelectEditor()
  8.     {
  9.     }
  10.     public override System.Drawing.Design.UITypeEditorEditStyle GetEditStyle 
  11.          (System.ComponentModel.ITypeDescriptorContext context)
  12.     {            
  13.         return UITypeEditorEditStyle.Modal;
  14.     }
  15.     public override object EditValue(System.ComponentModel.ItypeDescriptor 
  16.         Context context, System.IServiceProvider provider, object value)
  17.     {
  18.         IWindowsFormsEditorService service = (IWindowsFormsEditorService) 
  19.       provider.GetService(typeof(IWindowsFormsEditorService));
  20.         if (service == null)
  21.         {
  22.             return null;
  23.         }
  24.         
  25.         FormKeyBoard form = new FormKeyBoard();
  26.         
  27.         if (service.ShowDialog(form) == DialogResult.OK)
  28.         {
  29.             object strReturn = form.strReturnValue;
  30.             return strReturn;
  31.         }
  32.         
  33.         return value;
  34.     }
  35. }

此类的功能是弹出一个模式的计算器Form窗体,与4.5.2.2节定义的编辑器功能几乎同样,这里就不做多讲,若是还有不明白的地方请回顾一下前面章节的内容。

最后定义主控件代码类,以下所示:

/// <summary>

/// 得到本书更多内容,请看:

/// http://blog.csdn.net/ChengKing/archive/2008/08/18/2792440.aspx

/// </summary>

 

[DefaultProperty("Money")]

[ToolboxData("<{0}:CalculatorSelectControl

    runat=server></{0}:CalculatorSelectControl>")]

public class CalculatorSelectControl : WebControl

{

    [Bindable(true)]

    [Category("自定义计算机属性")]

    [DefaultValue("")]

    [Localizable(true)]

    [Editor(typeof(CalculatorSelectEditor), typeof(System.Drawing.Design. UITypeEditor))]

    [Description("请输入金额")]

    public string Money

    {

        get

        {

            string s = (string)ViewState["Money"];

            return ((s == null) ? "" : s);

        }

 

        set

        {

            ViewState["Money"] = value;

        }

    }

    //… …

}

 

u  主控件中定义了一个表示金额的Money属性,并指定设计时的编辑器为咱们上面定义的计算器编辑器类:

[Editor(typeof(CalculatorSelectEditor), typeof(System.Drawing.Design.UITypeEditor))]

编译此控件,在属性窗口中单击属性旁边的“”按钮便可以看到咱们定义的计算器编辑器,如图4-27所示。

 

4-27  属性窗口

 

本节内容已经讲解完。本节主要实现了几个自定义功能的编辑器:多类型子项集合编辑器、弹出式模态数据选择编辑器、下拉式数据选择编辑器,最后综合运用前面的知识实现了一个计算器编辑器。限于篇幅,示例都比较简单,但已经足以说明其用法了。另外,在定义本身的编辑器时,除了使用编辑器基类外,还能够把4.5.1节列出的那些系统编辑器做为基类使用。

4.6  类型转换器

类型转换器是什么?它主要完成什么样的功能呢?类型转换器可用于在数据类型之间转换值,并经过提供文本到值的转换或待选值的下拉列表来帮助在设计时配置属性。若是配置正确,经过使用InstanceDescriptorSystem.Reflection对象来给设计器序列化系统提供生成在运行时初始化属性的代码所需的信息,类型转换器能够生成属性配置代码。

类型转换器可用于字符串到值的转换,或用于在设计时和运行时数据类型之间的双向翻译。在宿主(如窗体设计器中的属性浏览器)中,类型转换器容许以文本形式向用户表示属性值,而且能够将用户输入的文本转换为相应数据类型的值。

大多数本机数据类型(Int32String、枚举类型和其余类型)具备默认的类型转换器,提供从字符串到值的转换并执行验证检查。默认的类型转换器位于System.ComponentModel命名空间中,名为TypeConverterNameConverter。当默认功能没法知足须要时,能够扩展类型转换器;当定义的自定义类型没有关联的类型转换器时,能够实现自定义类型转换器。

4.6.1  系统类型转换器

系统默认提供了许多经常使用的类型转换器,其中有很多咱们在使用控件时已经用到了。本节主要列举一些经常使用的转换器,并以其中几个经典的转换器为例说明其使用方式。

4.6.1.1  整型类型转换器

/// <summary>

/// 得到本书更多内容,请看:

/// http://blog.csdn.net/ChengKing/archive/2008/08/18/2792440.aspx

/// </summary>

 

[TypeConverter(typeof(Int32Converter))]

public int IntConverter

{

    //… …

}

转换器类名为Int32Converter,用于32位有符号整数对象与其余类型之相互转换的类型转换。它的展现样式与通常字符串属性彻底同样,只是假如咱们输入非整型的值,它会提示格式不正确,要求从新输入,例如输入一个字符“a”,会弹出如图4-28所示的提示输入格式错误的窗口。

 

 

4-28  类型不匹配提示窗口

 

在实际应用中,像stringint等类型的属性不须要咱们指定转换器,它会自动关联系统默认的转换器,即上面的[TypeConverter(…)]语句能够去掉。

这里要说明的重点是,为属性增长转换器的方法:

[TypeConverter(typeof(Int32Converter))]

该方法在属性上方增长TypeConverter设计时属性,参数为转换器的类型(系统提供的或自定义的),后面小节会介绍怎样为特性的属性类型定制自定义转换器。

4.6.1.2  WebColor类型转换器

[TypeConverter(typeof(WebColorConverter))]

public Color WebColorConverter

{

    //… …

}

 

属性窗口中显示效果如图4-29所示。

 

 

图4-29 颜色类型转换器

 

这也是系统提供的一个经常使用转换器,转换器类为WebColor Converter,注意单击下拉的颜色选择面板不是WebColorConverter提供的,是由默认的颜色编辑器提供(在4.5.1.2节有讲解),WebColorConverter主要用于设计或运行时从WebColor类型到字符串类型的转换。WebColorColor相比,提供了更多表示颜色格式的类型。

4.6.1.3  控件ID列表类型转换器

若是在设计器中的某几个控件具备关联关系,好比一个控件在运行时要获取另外一个控件的一些属性值,则能够用控件列表转换器ControlIDConverter来创建两个控件之间的关联关系。

[TypeConverter(typeof(ControlIDConverter))]             

public string TargetControl

{

    //… …

}

转换器类型为System.Web.UI.WebControls.ControlIDConverter,指定此类型转换器类型的属性展现效果如图4-30所示。

 

4-30  属性展现效果

 

在属性列表中,已经列出了设计器中其余几个控件的ID。在实际应用中,知道了控件的ID,就能够经过FindControl方法获取到整个控件了,FindControl使用方法在第3章有讲解。

除了上面介绍的三个外,系统还提供了好多转换器,限于篇幅就不一一做讲解,请看表4-1

在实际开发时,能够参考上面列表选择适合的类型转换器。提醒一点,上面的转换器除了能够用做开发控件设计时的属性类型转换器;也能够在其余地方直接使用类转换器中的功能方法,即把类型转换器做为普通类使用,这种方式也用得比较普遍,在后面讲解视图状态机制时就利用了自定义类型转换器(接下来要讲的SolidCoordinateConverter类型转换器)对视图对象进行正反序列化。

4-1  系统转换器

系统转换器类型

   

Int32Converter

32位有符号整数对象与其余表示形式相互转换

Int64Converter

64位有符号整数对象与各类其余表示形式相互转换

Int16Converter

16位有符号整数对象与各类其余表示形式相互转换

续表 

系统转换器类型

   

ByteConverter

字节类型与其余类型相互转换

BooleanConverter

Boolean对象与其余各类表示形式相互转换

CharConverter

Unicode字符对象与各类其余表示形式相互转换

UnitConverter

Unit对象转换为其余数据类型的对象,或从其余类型转换为 UNIT对象

EnumConverter

Enum对象与其余各类表示形式相互转换

DateTimeConverter

将日期类型与其余类型相互转换

DecimalConverter

Decimal对象与其余各类表示形式相互转换

StringConverter

在字符串对象与其余表示形式之间实现相互转换

DoubleConverter

将双精度对象与其余各类表示形式相互转换

SingleConverter

将单精度浮点数字对象与各类其余表示形式相互转换

TimeSpanConverter

TimeSpan对象与其余表示形式相互转换

WebColorConverter

在预约义的颜色名称或RGB颜色值与System.Drawing.Color 对象之间相互转换

ArrayConverter

Array对象与其余各类表示形式相互转换

CollectionConverter

将集合对象与各类其余表示形式相互转换

ExpandableObjectConverter

在可扩展对象与其余各类表示形式之间实现转换

GuidConverter

Guid对象与其余各类表示形式相互转换

BaseNumberConverter

为非浮点数字类型提供基类型转换器,上面几个整型转换器就是今后类派生的

ReferenceConverter

将对象引用与其余表示形式相互转换

TypeListConverter

以可用类型填充列表框的类型转换器

ObjectConverter

Object类型与其余类型相互转换

PropertyConverter

用于在属性值和字符串之间进行转换的转换器

DataBindingCollectionConverter

DataBindingCollection 对象的类型转换器

DataFieldConverter

可从当前组件的选定数据源中检索数据字段的列表

DataMemberConverter

可从当前组件选定的数据源中检索数据成员的列表

DataSourceConverter

数据源类型转换器

CursorConverter

Cursor对象与其余各类表示形式相互转换

FontNamesConverter

包含字体名称列表的字符串转换为包含个别名称的字符串数组,它还执行反转功能

FontUnitConverter

转换字体单位类型

StringArrayConverter

在以由逗号分隔的值组成的字符串与字符串数组之间进行转换

ControlIDConverter

控件ID列表转换器,4.6.1.3小节已经做过示例

TargetConverter

将从Web导航产生的内容的位置(目标)的值转换为字符串。该类还将字符串转换为目标值

ValidatedControlConverter

初始化ValidatedControlConverter类的新实例

4.6.2  定制本身的类型转换器

系统已经提供了不少的类型转换器,可以知足通常状况下开发的须要。但开发控件时,并非全部的属性类型都是那些简单的且系统已知的int, string等类型,即控件的属性类型能够是咱们定义的任意类型,所以系统不可以自动检测到该使用哪一个类型转换器,这种状况就须要咱们为本身的属性定制专门的类型转换器。

实现本身的类型转换器,通常须要如下5个步骤:

 定义一个从TypeConverter派生的类,TypeConverter类提供了将值的类型转换为其余类型,以及访问标准值和子属性的统一方法。其主要是重载类的一些正反向转换方法。

 重写CanConvertFrom方法,在方法中指定是否能够从字符串转换为指定的类型。

 重写ConvertFrom方法,实现从字符串到指定类型的转换。

 重写CanConvertTo方法,指定是否能从SolidCoordinate类转换为stringInstanceDescriptor类型InstanceDescriptor是提供建立对象实例所需信息的类。转换为字符串类型不须要重写此方法。

 重写ConvertTo方法,实现转换。

其中上面2345都是重载方法。下面就以两个例子说明类型转换器的建立过程。

4.6.2.1  三维坐标类型转换器

家都知道在.NET Framework中有Point类,若是把该类做为属性的类型,则系统会自动调用它的类型转换器进行类型转换。好比在属性窗口中设置属性值,切换到源代码视图时即调用类型转换器进行转换;或在运行时控件状态或视图状态对存储的对象进行序列化和反序列化。

 

这里咱们定义一种新的坐标类型SolidCoordinate类,并为其定义匹配的类型转换器,以此说明如何自定义和使用类型转换器。

来看一下实现后的效果,在属性窗口中设置SolidCoordinate类型的属性,如图4-31所示。

 

图4-31 三维坐标类型转换器

 

而后,切换到源代码视图,则会看到以下代码:

<cc1:CustomeTypeConverterControl ID="CustomeTypeConverterControl1" runat= "server" SolidCoordinate="3, 5, 8" />

在切换到源代码视图时,转换器类就起做用了,它会把 SolidCoordinate转换成字符串类型,由于在源代码模式下全部代码类型只能以字符串格式存在,因此要求转换为字符串格式;反之,会把字符串逆向转换为SolidCoordinate类。这就是类型转换器的功能。

格式SolidCoordinate="3,5,8"是能够自定义的,好比能够定义成SolidCoordinate="3-5-8"格式,规则能够在转换器类中任意指定,只要是字符串格式且保证正反向转换规则一致便可。

接下来开始讲解代码部分。SolidCoordinate类共有三个属性(XYZ),前两个值(XY)与Point类型的(XY)属性一致,表示平面上的横坐标和纵坐标;(Z)属性表示平面以外的第三维坐标,类代码以下:

/// <summary>

/// 得到本书更多内容,请看:

/// http://blog.csdn.net/ChengKing/archive/2008/08/18/2792440.aspx

/// </summary>

 

[TypeConverter(typeof(SolidCoordinateConverter))]

public class SolidCoordinate

{

    private int x;

    private int y;

    private int z;

 

    public SolidCoordinate()

    {       

    }

 

    public SolidCoordinate(int x, int y, int z)

    {

        this.x = x;

        this.y = y;

        this.z = z;

    }

 

    [NotifyParentProperty(true)]

    public int X

    {

        get

        {

            return this.x;

        }

        set

        {

            this.x = value;

        }

    }

 

    [NotifyParentProperty(true)]

    public int Y

    {

        get

        {

            return this.y;

        }

        set

        {

            this.y = value;

        }

    }

 

    [NotifyParentProperty(true)]

    public int Z

    {

        get

        {

            return this.z;

        }

        set

        {

            this.z = value;

        }

    }

}

u  类代码就包括三个坐标属性,没有任何方法。须要注意的是上面有句:

[TypeConverter(typeof(SolidCoordinateConverter))]

其做用是指定该类的转换器为SolidCoordinateConverter,即凡是SolidCoordinate类型的控件属性都会使用此类型转换器。

SolidCoordinateConverter类的源代码以下:

 

  1. /// <summary>
  2. /// 得到本书更多内容,请看:
  3. /// http://blog.csdn.net/ChengKing/archive/2008/08/18/2792440.aspx
  4. /// </summary> 
  5. public class SolidCoordinateConverter : TypeConverter
  6. {
  7.     public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
  8.     {
  9.         return ((sourceType == typeof(string)) || base.CanConvertFrom(context, sourceType));
  10.     }<