★★★【庖丁解牛:縱向切入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.     }
  11.     public </
相關文章
相關標籤/搜索