本章內容
4.1 控件屬性的做用
4.2 簡單屬性
4.3 屬性的設計時特性
4.4 複雜屬性
4.5 深刻研究——定製本身的屬性編輯器
4.6 類型轉換器
4.7 實現自定義屬性數組
|
當開發控件時若是選擇基類,好比選擇繼承WebControl基類,一旦繼承於此類,一些默認的系統屬性就會成爲當前控件的屬性集的一部分,圖4-1所示的是WebControl類的系統屬性。緩存
能夠看到一個通用Web控件所應具有的基本屬性都已經有了,在實際開發控件時選擇某個基類。
4.1.1節所講的是系統已有的屬性,在開發控件時通常都要爲本身的控件增長一些自定義屬性。自定義屬性與系統屬性徹底同樣。只是因爲不具備系統屬性的通用性而須要開發者本身去實現。下面看一下屬性的語法格式:
string strText = "默認值";
public string Text
{
get
{
return strText;
}
set
{
strText = value;
}
}
以上是一個最簡單的屬性,由一個set和get語段組成。注意,set和get段不是必需的,好比能夠去掉set段表示此屬性只容許讀取而不容許接收值。
事實上屬性的特性範疇還比較多,如簡單屬性、複雜屬性,以及屬性在設計時的特性和標記形式的格式等,下面將對這些特性一一進行介紹。
簡單屬性是類型爲字符串的或容易轉換爲字符串的屬性。簡單屬性在控件的開始標記上自行保留爲屬性。.NET Framework 類庫中的基元值類型,如String、Boolean、Int16、Int32、DateTime、Byte、Char、Double和Enum均爲簡單屬性。能夠經過添加代碼將簡單屬性存儲在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>
一樣,Boolean、Int16、Int32、DateTime、Byte、Char、Double和Enum等類型的屬性與上面的String類型屬性代碼標記徹底同樣。簡單屬性比較簡單,就講解到這裏。
.NET Framework爲控件設計時屬性提供了不少豐富的類,這些屬性的功能很是靈活,控制範圍普遍,好比能夠控制該屬性在屬性窗口中的顯示模式,如:是否在屬性窗口中顯示該屬性,也能夠指定此屬性必須接收值類型描述,按組分類等,也能夠控制文本的標記呈現格式等,甚至能夠本身定義一個屬性類,實現本身想實現的功能。下面講一下經常使用的.NET Framework的屬性類對控件的支持功能。
Ø Bindable
指定屬性是否能夠綁定一個有效數據源,一般使用布爾值進行設置。例如:Bindable(true)。若是使用值true標記屬性,表示該屬性能夠綁定一個有效數據源。
Ø Browsable
指定屬性是否應該在屬性窗口中顯示,使用布爾值設置。通常狀況下,對於經常使用的和比較重要的屬性設置Browsable爲true,不然,設置Browsable爲false。
Ø 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種枚舉設置方式:
PersistenceMode(PersistenceMode.Attribute)指定屬性或事件保持爲屬性;
PersistenceMode(PersistenceMode.EncodedInnerDefaultProperty)指定屬性做爲服務器控件的惟一內部文本,若是屬性值是HTML編碼的,只能對字符串做這種指定;
PersistenceMode(PersistenceMode.InnerDefaultProperty)指定屬性在服務器控件中保持爲內部文本,還指示將該屬性定義爲元素的默認屬性,只能指定一個屬性爲默認屬性;
PersistenceMode(PersistenceMode.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
設計時特性,指定與控件關聯的設計器類。控件設計器類用於控制關聯的控件在可視化設計器的設計圖面上的外觀和行爲。
還有一些更復雜的,包括在設計模式下的元數據屬性類在這裏沒有列出,由於在後面有專門的章節詳細介紹,經過代碼示例更容易理解。在這裏只要理解上面這些屬性類功能,開發通常的控件是沒有問題了。
複雜屬性是屬性的類型不是簡單值類型,或者是一個包含其餘屬性的類。例如.NET Framework中的Style,Font,Point等都是複雜屬性。另外還有集合屬性,這裏也將它做爲複雜屬性歸類,對於集合屬性在本章後面會單獨拿出來一節進行詳細講解。
先看看一個典型的代碼段:
<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控件的一些屬性。仔細觀察一下這些控件的屬性標記,咱們能很容易給它們歸類,好比:GridView的ID/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的屬性,將如下代碼增長到控件中:
此屬性與簡單屬性的區別有兩點:第一,屬性接收和返回的類型不是簡單類型(int,string 等),而是用咱們本身定義的Person類;第二,複雜屬性通常沒有set語句,由於通常是對複雜屬性的子屬性(或子對象)賦值,只要保證它的子屬性(子對象)中具備get/set語句便可。編譯此控件,在IDE中打開頁面,並打開控件的屬性窗口,會看到如圖4-3所示的界面。
圖4-3 複雜屬性
|
解決辦法是,爲主控件屬性Person增長PersistenceMode和DesignerSerializationVisibility兩個設計特性,片斷代碼以下所示:
1.PersistenceMode特性
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
表示用屬性名稱做爲嵌套標籤表示複雜屬性,好比GridView的HeaderStyle屬性,就是使用了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>編碼爲<div>;,即要保證其內容不能再存儲HTML標記和子標籤。
此特性表示指定在設計時序列化複雜對象的方式。它有三個枚舉類型:
Ø 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,以下所示:
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,定義其內容以下:
注意不要漏掉TypeConverter和NotifyParentProperty,其用途在前面中已經講過了。
再在主控件中增長一個RowStyle類型的屬性,屬性名爲RowStyle,增長後的屬性代碼片斷以下所示:
選擇PersistenceMode特性的InnerProperty枚舉項,表示生成嵌套標記;至於DesignerSerializationVisibility特性,依然選擇Content枚舉值,這裏也是對複雜屬性RowStyle的類對象子屬性進行序列化。如還不清楚這兩個屬性的使用,請到前面的4.1.1節回顧一下。
而後,在主控件加兩個類特性,以下所示:
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的容器以及一些經常使用的添加/刪除等子項方法。
此子項類的代碼比較簡單,惟一要說明的是上面的[ToolBoxItem(false)]表示不在IDE工具箱的控件集合中顯示。很顯然這不是一個控件,不能在工具箱集合列表中顯示,通常除了主控件以外的其他類都要把ToolBoxItem類元數據特性置爲false,不然當使用者拖一個不完整的控件標記到頁面上時可能出現控件不能使用的狀況。
這裏的Items採用泛型集合,繼承list<T>強類型集合做爲基類,此外在System.Collections. Generic命名空間中還有其餘一些強類型集合。
增長完上面兩個類後,實現內部默認集合屬性,還須要設置兩個類設計特性:一是在控件類前設置ParseChildren(true,「默認屬性名稱」),指定主控件中的屬性名稱表示是屬性,而不是子控件,ParseChildren在4.3節已經作了講解;二是設置[PersistChildren(false)]類特性,表示要把集合標記做爲屬性方式保持和進行序列化。
在主控件的集合屬性前要設置以下三個特性:
[PersistenceMode(PersistenceMode.InnerDefaultProperty)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
[NotifyParentProperty(true)]
第一個特性,指定集合屬性爲內部默認屬性;第二個特性,指定要序列化的是集合屬性的內容,而不是集合屬性自己;第三個特性,指定集合屬性的子屬性修改時會通知父屬性。
新建一個Web自定義控件文件,並按以上所述進行設置,控件主類核心代碼以下:
除了設置上面所提到的屬性外,本集合類還多了一個特性[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標記或非子控件的純文本,本節主要完成這種格式屬性的實現。
爲了保留前面控件已有的功能,從新定義兩個類Item2和ListItem2。
代碼以下:
代碼以下:
此子項類要作一些特性設置,類元特性須要增長兩個特殊的特性:
Ø [ParseChildren(true, "Text")]
Ø [PersistChildren(false)]
第一個特性表示將子Text元素做爲服務器控件的屬性分析;第二個特性表示將該控件的屬性做爲嵌套元素保持。
另外,還要注意要對做爲編碼內部屬性的屬性進行設置,好比這裏爲Text屬性加上:
[PersistenceMode(PersistenceMode.EncodedInnerDefaultProperty)]
u 進行如上設置後,增長一個主控件文件,並進行以下所示設置:
上面主控件類與 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.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來定製本身的頁面解析子控件方法。主控件核心源代碼以下:
本主控件類包含三個集合複雜屬性:兩個內部默認嵌套形式屬性(Items和Items2);一個內部編碼嵌套形式屬性(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>
能夠看到,ListItem,ListItem2,ListItem3很是有序地嵌套在主控件內部,從而實現了主控件內部多個複雜默認屬性嵌套功能。
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.cs和ScriptItemCollection.cs,分別定義ScriptItem類和ScriptItemCollection類。其中,ScriptItem類主要存儲用戶自定義的客戶端腳本命令(JavaScript塊),ScriptItemCollection能夠定義一個集合容器,每一個項都是一個 ScriptItem項。與前面講的集合實現很是相似。這兩個類的完整代碼以下:
該類中的Text就是用於存儲用戶定義的腳本塊;Editor元數據特性指定在屬性窗口中Text屬性的編輯器是一個下拉塊輸入編輯器,關於屬性編輯器下一節會詳細講解,這裏僅知道它的功能便可。
須要注意的是,在上一節使用AddParsedSubObject實現頁面解析子控件時,要嵌套的三個集合的子標記:ListItem,ListItem2,ListItem3都繼承了Control基類,目的是把這些子標記做爲子控件(也就具備了前綴和runat屬性),而這裏的ScriptItem沒有繼承任何基類,這樣就避免了繼承一些基類中的冗餘屬性和方法。
/// <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類,能夠直接繼承該類並實現本身的方法,已經預先實現好了的構造器類代碼以下所示:
在該類中要作的最重要的事情是重寫方法GetChildControlType,在頁面解析器分析主控件的每一個子標記時,都會調用一次此方法。
該方法的第一個參數表示當前正在解析的控件標記字符串,第二個參數表示標記上全部特性的字典集合。方法體中的if語句的功能是,假如當前解析的標記是「scriptitem」(就是後面定義到主控件的集合屬性名稱),則返回ScriptItem類的類型,且經過ToLower()方法實現不區分大小寫。須要注意的是,這裏咱們作的工做很是簡單,只是匹配相應的字符串標記並返回一個類型。而AddParsedSubObject則要本身處理當前對象的值。還有個重寫方法AllowWhitespaceLiterals用於指定控件的開始標記和結束標記之間是否容許存在空白。
定義完本身的構造器後,經過爲主控件增長以下元數據特性,指定主控件的解析器:
[ControlBuilder(typeof(ScriptItemBuilder))]
u 設置完後,完整的主控件類源代碼以下:
整個主控件只包括一個集合屬性。須要注意的是咱們把這個屬性定義成內部嵌套標記形式,即咱們在<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的繼承類。這兩種方式主要是在實現頁面中有多個默認嵌套屬性時使用。
(2)AddParsedSubObject實現比較簡單,僅實現一個方法,通常用於複雜屬性單一且比較少的狀況。
(3)實現ControlBuilder的定製構造器類比重載AddParsedSubObject要麻煩些,但功能更強,能處理更靈活的嵌套標記。AddParsedSubObject最大的限制是它的內部必須是子控件類型。
(4)兩種方式都是ASP.NET提供的兩種解決方案,都有它們使用的場景,能夠根據本身喜愛選擇,當習慣使用構造器後,會發現構造器功能更強大、更靈活,用起來更順手,它能夠徹底替代重載AddParsedSubObject方式。
到如今爲止基本上已經把咱們見過的全部的屬性標記格式都實現了一遍,4.4.3.2節也把平時不多用到的定製頁面解析器功能詳細地講解了一下,其中有些標記在日常開發中比較少用到。本章能夠做爲查找手冊使用,何時用到這些內容,何時過來查便可。下一節會有更精彩的內容。
對於控件的全部屬性,若是都提供很是友好的屬性編輯器,使用者使用起來會更加方便。本節主旨就是講解一下控件屬性編輯器,4.5.1節提供一些系統通用編輯器;在複雜屬性中,集合屬性是最重要的最經常使用的屬性,4.5.2節將主要講解怎樣定製複雜集合類型編輯器以及一些特殊比較酷的編輯器類型。
這些設計器的執行主要是在設計模式下,直接與IDE交互,在編程時能夠直接使用System.Windows命名空間開頭的一些命名空間下的類。這裏首先加入幾個本節須要使用到的引用。右擊控件庫工程,選擇「添加引用」命令,如圖4-9所示。
選擇「添加引用」命令後會打開「添加引用」對話框,如圖4-10所示。
圖4-9 添加引用
圖4-10 「添加引用」對話框
在對話框中找到如下三個引用程序集:
(1)System.Designer
(2)System.Drawing.Design
(3)System.Windows.Forms
單擊「肯定」按鈕,這樣在須要使用的地方打開程序集中的命名空間,就可使用程序集中的系統類了。
頗有必要用一小節講解一下系統提供的一些編輯器。讀者對這些編輯器可能都比較熟悉,但它們是怎麼使用的呢?其實使用都很簡單,僅在每一個須要配置的屬性前面指定一個標誌某種屬性編輯器的元數據特性便可。下面就分別介紹一下它們。
4.5.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特性就是指定屬性編輯器類型,後面的幾種系統屬性編輯器類型也是如此。
圖4-11 多行下拉文本屬性編輯器
4.5.1.2 色值選擇屬性編輯器
[Editor("System.ComponentModel.Design.ColorEditor,System.Design", typeof(UITypeEditor))]
public Color ColorEditor
{
//... ...
}
圖4-12 色值選擇屬性編輯器
4.5.1.3 文件選擇屬性編輯器
[Editor(typeof(FileNameEditor), typeof(UITypeEditor))]
public string FileName
{
//... ...
}
圖4-13 文件選擇屬性編輯器
上圖即爲單擊屬性窗口中「文件名屬性」按鈕彈出的「文件選擇屬性編輯器」對話框,其實也是調用的Windows系統的「打開文件」對話框。
4.5.1.4 目錄選擇屬性編輯器
[Editor(typeof(FolderNameEditor), typeof(UITypeEditor))]
public string FolderNameEditor
{
//... ...
}
圖4-14 目錄選擇屬性編輯器
4.5.1.5 鏈接字符串屬性編輯器
[Editor(typeof(System.Web.UI.Design.ConnectionStringEditor), typeof(UITypeEditor))]
public string ConnectionStringEditor
{
//... ...
}
圖4-15 鏈接字符串屬性編輯器
4.5.1.6 表達式綁定集合屬性編輯器
[Editor(typeof(System.Web.UI.Design.ExpressionsCollectionEditor), typeof(UITypeEditor))]
public string ExpressionsCollectionEditor
{
//... ...
}
圖4-16 表達式綁定集合屬性編輯器
在此窗口能夠爲屬性指定要綁定到應用程序的配置文件、鏈接字符串或者資源文件。
4.5.1.7 用戶控件對話框編輯器
[Editor(typeof(System.Web.UI.Design.UserControlFileEditor), typeof(UITypeEditor))]
public string UserControlFileEditor
{
//... ...
}
圖4-17 用戶控件對話框編輯器
此窗口用於選擇當前站點下的用戶控件文件(*.ascx),且默認的可選擇路徑不像文件和目錄選擇是本計算機硬盤,而是當前站點。
主控件的完整源代碼以下:
這些代碼比較簡單,就不做解釋了。以上僅列出一些可能會常常用到的屬性編輯器,系統提供的屬性編輯器不止這些,像前面講的集合也是使用系統默認的集合屬性編輯器。其餘的屬性編輯器能夠在使用的過程當中慢慢研究。下一節經過幾個例子詳細講一下怎樣定製本身的屬性編輯器。
系統提供了不少屬性編輯器,可以知足絕大多數複雜度不是很高的控件的須要。這一節主要經過四個小例子講解怎樣定製個性化的屬性編輯器,其實只要可以想到,咱們就可以作到。
4.5.2.1 定製多類型集合屬性編輯器
前面咱們在爲控件增長集合屬性時,默認狀況下會使用系統默認的集合編輯器,而咱們這裏自定義的集合屬性編輯器功能更強,先看一下它實現後的效果圖,如圖4-18所示。
能夠看到,該編輯器除了可以實現基本的集合屬性編輯功能外,經過單擊「添加」按鈕右邊的下拉按鈕還能夠選擇添加項的類型,即咱們能夠定義任意個不一樣類型的集合項做爲屬性集合內容,這裏咱們定義兩個集合子項類別:CommonItem(子項)和CommandSeperator(分隔符),以它們做爲示例講解。下面就來看一下它的實現過程。
圖4-18 定製多類型集合屬性編輯器
因爲涉及集合,首先仍是定義幾個與集合實現相關的類。定義一個抽象類ItemBase,表示每一個集合子項類的子類,任何集合子類都要繼承該類。其類代碼以下所示:
這裏爲了簡化代碼,只定義了一個EnableViewState的屬性,表示是否啓用視圖狀態,在使用時還能夠繼承Control等基類,使用Control等類的類成員。這裏要注意的是此類定義成了抽象類,此類不會單獨生成實例添加到集合中,也不會被做爲一種集合子類型顯示到屬性編輯窗口中供用戶選擇,由於ItemBase在這裏沒有表示具體的集合子項,也不具備集合子項意義。全部集合子類型最終是以它們的基類型ItemBase添加到集合中統一管理的。
下面定義一個具體的集合子項類CommonItem,表示一個按鈕類型,類結構代碼以下:
類代碼很簡單,主要包括描述按鈕的一些基本信息:文本、快捷鍵、提示、可用性以及按鈕類型(新增/保存/刪除等)。這裏僅須要注意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類型,表示分隔符類型,即一組按鈕與另外一組按鈕之間的分隔符。好比:「首頁」、「上一頁」、「下一頁」、「末頁」,這是一組具備相似功能的一級按鈕。另外一組:「新增」、「修改」、「刪除」、「查看」屬於一組功能相似按鈕,這兩組按鈕之間須要用某個分隔符分開,這樣可使使用者更容易區分各個按鈕的功能,外觀佈局也不會顯示零亂。類代碼以下所示:
此分隔符類僅包括兩個屬性:寬度和高度。另外,它也繼承了ItemBase抽象類。
到如今爲止,已經定義完四個類:抽象基類(ItemBase),按鈕類(CommandItem),分隔按鈕類(CommandSeperator),以及一個功能枚舉類(CommandActionType)。而後就能夠定義一個存儲以上各個按鈕類型的集合類了,類名爲CommandCollection,此集合爲強類型集合類,類代碼以下:
該集合類繼承Collection<ItemBase>類,表示強類型集合,且每一個子項的類型爲ItemBase,從這裏能夠想到咱們上面定義的兩個子項類CommandItem和CommandSeperator都要繼承於ItemBase的緣由了。[ParseChilderen(true)]表示把當前屬性做爲主控件的屬性(而非子控件)進行解析。
本節的重點,也是最重要的一個屬性[Editor(typeof(CommandCollectionEditor),typeof (UITypeEditor))],表示指定此集合類的集合編輯器爲CommandCollectionEditor,即在主控件中凡是定義爲CommandCollection類的屬性都會把CommandCollectionEditor做爲它的編輯器。下面詳細介紹一下編輯器類是怎麼使用的。仍是先看一下 CommandCollectionEditor編輯器類的源代碼:
實現一個集合編輯器通常要繼承System.ComponentModel.Design.CollectionEditor,並重寫該類的一些方法。下面分別說一下各個方法的做用。
Ø 集合編輯器中的構造方法 CommandCollectionEditor ,主要完成自定義的初始化功能。該方法中的參數返回該編輯器做用的對象實例(在這裏是CommandCollection的一個對象實例),能夠取到當前CommandCollection對象的全部數據。
Ø 方法CanSelectMultipleInstances的返回值表示是否可以在編輯窗口選擇多個實例,這裏設置返回true。
Ø 重寫方法CreateNewItemTypes,返回咱們定義的兩個集合類型:
return new Type[] { typeof(CommandItem), typeof(CommandSeperator) };
CommandItem和CommandSeperator是咱們定義的兩個集合類型。在單擊主控件屬性窗口中集合屬性編輯器「…」形狀按鈕時,此方法執行,把全部定義的類型加載到系統集合中緩存起來,而後根據此集合值在編輯器界面中呈現可能選擇類型的列表。
Ø CreateInstance方法主要是負責創建一個集合子項類型實例。
至此全部功能類都創建完成了,最後創建主控件類,並應用CommandCollection集合類做爲控件的一個屬性,代碼以下所示:
主控件定義了一個重要的類元數據特性[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.cs和ategoryWindow.Designer.cs。CategoryWindow.Designer.cs主要存儲窗體和內部控件內容信息;CategoryWindow.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;
以上語句表示:單擊「肯定」按鈕則返回選中的數據給父窗體;單擊「取消」按鈕則不返回數據。
該頁面沒有複雜的交互邏輯,僅在類構造方法中調用SetSelectData方法爲窗體中的ComboBox控件綁定食品數據列表。這裏限於篇幅僅作了一個儘可能簡單的窗體,在實際開發中還能夠定製任意複雜的窗體,在本節最後還提供了一個比較複雜的能夠實現計算器功能的模態窗體。
數據選擇窗體已經創建好以後,再建立控件的屬性編輯器文件,該編輯器文件中的類主要用於調用上面建立的數據選擇窗體,包括打開窗體,選擇完數據後,接收值並賦給屬性窗口的對應屬性。在講解源代碼以前,要先打開幾個命名空間:
using System.Drawing;
using System.Drawing.Design;
using System.Windows.Forms;
using System.Windows.Forms.Design;
u 這些命名空間主要是提供控件對WinForm的設計模式支持。下面仍是先看一下此編輯器類的代碼:
這裏使用的編輯器基類與前面咱們定義的集合編輯器不同,前面集合編輯器是使用System.ComponentModel.Design下的集合基類,這裏使用的是System.Drawing.Design下的UItypeEdit基類。在實際開發中能夠任意選擇系統提供的編輯器基類,在4.5.1節已經列出了不少基類,也能夠直接繼承這些類定製本身的編輯器。
方法GetEditStyle的System.ComponentModel.ITypeDescriptorContext類型參數,表示要轉換的對象的上下文;方法GetEditStyle中UITypeEditorEditStyle.Modal的UITypeEditorEditStyle枚舉表示以什麼樣的形式打開編輯窗體,它有三個枚舉值:Modal,DropDown,None。其中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枚舉項,才真正把窗體中當前ComboBox的SelectedItem項返回。看到這裏,咱們可能會想起前面把數據選擇窗體中的「肯定」和「取消」兩個按鈕的DialogResult屬性值分別設置爲DialogResult.OK和DialogResult.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.cs和CategoryDropDown.Designer.cs,CategoryDropDown.cs主要供開發人員完成交互邏輯;CategoryDropDown.Designer.cs主要存儲窗體和內部控件內容信息,下面分別來看一下它們的源代碼。
這裏沒有像模態編輯器示例同樣設置控件的DialogResult屬性,而是換了一種方式,分別爲「肯定」和「取消」兩按鈕定義事件,在事件中進行數據返回邏輯處理,關於事件將在接下來要講解的另外一個部分類中介紹。
/// <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,在下面講的編輯器中會獲取該變動的值,並賦值到屬性。
至此下拉界面已經定義完成,接下來定義編輯器類,代碼以下:
下拉編輯器與前面小節講的彈出式模態編輯器都經過繼承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的實現代碼:
上面代碼爲一個WinForm窗體,主要實現展現一個計算器功能。此計算器能夠一次性計算多個操做項的值。例如:y = 3 + 64 * (2 + 3^5) + sinPI的值,這是它與其餘計算器的不一樣之處,好比Windows自帶的計算器一次只能計算兩個操做數,這裏能夠支持您任意輸入多個操做數的表達式,最後一塊計算出結果。支持:sin,cos,tan,cot,arcsin,arccos,arctan,sec,csc,ln,^,*,/,+,-,(,)運算符,並用括號區分優先級。另外,此計算器的實現算法也不錯,採用經典的Stack編譯算法,感興趣的讀者能夠研究一下。
而後定義一個編輯器,其實全部這些的編輯器功能至關於一個「橋接器」,使屬性與自定義Form窗體關聯起來。代碼以下:
此類的功能是彈出一個模式的計算器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節列出的那些系統編輯器做爲基類使用。
類型轉換器是什麼?它主要完成什麼樣的功能呢?類型轉換器可用於在數據類型之間轉換值,並經過提供文本到值的轉換或待選值的下拉列表來幫助在設計時配置屬性。若是配置正確,經過使用InstanceDescriptor和System.Reflection對象來給設計器序列化系統提供生成在運行時初始化屬性的代碼所需的信息,類型轉換器能夠生成屬性配置代碼。
類型轉換器可用於字符串到值的轉換,或用於在設計時和運行時數據類型之間的雙向翻譯。在宿主(如窗體設計器中的屬性瀏覽器)中,類型轉換器容許以文本形式向用戶表示屬性值,而且能夠將用戶輸入的文本轉換爲相應數據類型的值。
大多數本機數據類型(Int32、String、枚舉類型和其餘類型)具備默認的類型轉換器,提供從字符串到值的轉換並執行驗證檢查。默認的類型轉換器位於System.ComponentModel命名空間中,名爲TypeConverterNameConverter。當默認功能沒法知足須要時,能夠擴展類型轉換器;當定義的自定義類型沒有關聯的類型轉換器時,能夠實現自定義類型轉換器。
系統默認提供了許多經常使用的類型轉換器,其中有很多咱們在使用控件時已經用到了。本節主要列舉一些經常使用的轉換器,並以其中幾個經典的轉換器爲例說明其使用方式。
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 類型不匹配提示窗口
在實際應用中,像string,int等類型的屬性不須要咱們指定轉換器,它會自動關聯繫統默認的轉換器,即上面的[TypeConverter(…)]語句能夠去掉。
這裏要說明的重點是,爲屬性增長轉換器的方法:
[TypeConverter(typeof(Int32Converter))]
該方法在屬性上方增長TypeConverter設計時屬性,參數爲轉換器的類型(系統提供的或自定義的),後面小節會介紹怎樣爲特性的屬性類型定製自定義轉換器。
4.6.1.2 WebColor類型轉換器
[TypeConverter(typeof(WebColorConverter))]
public Color WebColorConverter
{
//… …
}
|
圖4-29 顏色類型轉換器
這也是系統提供的一個經常使用轉換器,轉換器類爲WebColor Converter,注意單擊下拉的顏色選擇面板不是WebColorConverter提供的,是由默認的顏色編輯器提供(在4.5.1.2節有講解),WebColorConverter主要用於設計或運行時從WebColor類型到字符串類型的轉換。WebColor與Color相比,提供了更多表示顏色格式的類型。
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類的新實例 |
系統已經提供了不少的類型轉換器,可以知足通常狀況下開發的須要。但開發控件時,並非全部的屬性類型都是那些簡單的且系統已知的int, string等類型,即控件的屬性類型能夠是咱們定義的任意類型,所以系統不可以自動檢測到該使用哪一個類型轉換器,這種狀況就須要咱們爲本身的屬性定製專門的類型轉換器。
實現本身的類型轉換器,通常須要如下5個步驟:
其中上面2,3,4,5都是重載方法。下面就以兩個例子說明類型轉換器的建立過程。
4.6.2.1 三維座標類型轉換器
大家都知道在.NET Framework中有Point類,若是把該類做爲屬性的類型,則系統會自動調用它的類型轉換器進行類型轉換。好比在屬性窗口中設置屬性值,切換到源代碼視圖時即調用類型轉換器進行轉換;或在運行時控件狀態或視圖狀態對存儲的對象進行序列化和反序列化。
|
先來看一下實現後的效果,在屬性窗口中設置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類共有三個屬性(X,Y,Z),前兩個值(X,Y)與Point類型的(X,Y)屬性一致,表示平面上的橫座標和縱座標;(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類的源代碼以下: