在前面的系列文章中,筆者已經列舉了幾個實現自定義服務器控件的示例。通過這些示例,讀者初步接觸了有關創建服務器控件屬性的內容。例如,使用私有變量、視圖狀態、控件狀態等實現屬性等等。雖然讀者通過這些內容可以了解實現屬性的一些基本知識,但是這還是不夠的。從本節開始,將針對實現自定義服務器控件屬性的問題展開講解。本節重點介紹實現自定義服務器控件屬性的一些基本概念和簡單屬性的基本實現方法等內容。
1. 控件屬性基本概念
本小節介紹有關創建服務器控件屬性的基本內容,具體內容包括:(1)屬性類型和形式;(2)從control和webcontrol繼承的屬性;(3)與屬性相關的設計時元數據attribute。
1) 屬性類型和形式
通常情況下,服務器控件屬性可以分為兩種類型:簡單屬性和復雜屬性。
簡單屬性是指屬性值可以很容易轉換為字符串表達式的屬性,這種屬性的值通常為boolean、byte、char、double、enum、int32、datetime等簡單數值類型,以及string類型和枚舉類型。開發人員可以通過添加代碼,將簡單屬性存儲在viewstate字典中,以在回發間進行狀態管理。如果一個屬性的類型是本身具有屬性(稱為子屬性)的類,則該屬性就稱為復雜屬性。例如,webcontrol類的font屬性的類型是本身具有屬性(如bold和name)的fontinfo類。bold和name是webcontrol的font屬性的子屬性。asp.net頁框架可通過使用帶有連字符的語法(例如font-bold="true")在控件的開始標記上保存子屬性,但如果在控件的標記(例如<font bold="true">)中保存子屬性,則子屬性在頁中的可讀性更強。
在上文中談到了屬性的標記形式,即添加連字符的形式。實際上,不同的屬性表現出不同的標記形式。為了加深對簡單屬性和復雜屬性的認識,下面介紹一下有關屬性的4種標記形式。
· 通用形式屬性
這是一種最為常見的屬性標記形式。這種形式的屬性標記位于控件內部,與runat="server"一起定義。通常為以下形式:
<mycontrol:customercontrol id="demo1" runat="server" propertyname="propertyvalue"/>
其中propertyname為一個不帶連字符的單詞。例如:
<asp:button id="button1" runat="server" text="submit"/>
此處的屬性text屬于通用形式屬性。
· 連字符形式屬性
這種標記形式的屬性位于控件標記內部,帶有連字符是這種形式屬性的最大特征。其形式為:
<mycontrol:customercontrol id="demo1" runat="server" sub-propertyname="propertyvalue"/>
其中sub-propertyname為一個帶連字符的單詞組合。例如:
<asp:label id="label1" runat="server" font-size="medium" font-underline="true" />
在上面的代碼中,font-size和font-underline就是典型的連字符形式屬性。
· 內部嵌套形式屬性
凡是具有這種標記形式的屬性均為復雜屬性。它是以嵌套形式在控件標記內部聲明某屬性集的子屬性。其形式類似:
<asp:datagrid id="datagrid1" runat="server">
<headerstyle forecolor="#ffffcc" backcolor="#990000">
</headerstyle>
<footerstyle forecolor="#330099" backcolor="#ffffcc">
</footerstyle>
</asp:datagrid>
其中headerstyle是內部嵌套形式屬性,forecolor和backcolor是headerstyle屬性的子屬性。footerstyle與headerstyle是一樣的,也是內部嵌套形式屬性。
· 內部嵌套形式默認屬性
這種標記形式的屬性通常用于服務器控件的集合屬性,具有這種形式的屬性必然是復雜屬性。該形式屬性與上文所述"內部嵌套形式屬性"的標記形式基本相同。不同之處在于:當某控件具有這種屬性時,控件標記中只包含該形式屬性,不能包含其他任何屬性。這就是為什么稱為"默認"的原因。其形式類似:
<asp:dropdownlist id="dropdownlist1" runat="server">
<asp:listitem>1</asp:listitem>
<asp:listitem>2</asp:listitem>
<asp:listitem>3</asp:listitem>
<asp:listitem>4</asp:listitem>
</asp:dropdownlist>
其中屬性listitem就是典型的內部嵌套形式默認屬性。
2) 從control和webcontrol繼承的屬性
如前面文章所述,如果需要開發沒有ui的控件或者組合其他呈現它們自己的ui的控件,則從system.web.ui.control基類派生。為此,讀者應該了解一些control類的常見屬性。如表1列舉了control基類常用屬性,它們在開發服務器控件過程中經常被使用。
| 屬性 | 數據類型 | 說明 |
| controls | controlcollection | 獲取 controlcollection 對象,該對象表示 ui 層次結構中指定服務器控件的子控件 |
| adapter | controladapter | 獲取控件的瀏覽器特定適配器。(asp.net 2.0新增) |
| apprelativetemplatesourcedirectory | string | 獲取或設置包含該控件的 page 或 usercontrol 對象的應用程序相對虛擬目錄。(asp.net 2.0新增) |
| enabletheming | bool | 獲取或設置一個值,該值指示是否對此控件應用主題。(asp.net 2.0新增) |
| page | page | 獲取對包含服務器控件的 page 實例的引用。 |
| parent | control | 控件屬于其controls集合的控件。(如果控件b是a.controls的一個元素,則控件a是控件b的父級) |
| enableviewstate | bool | 指示控件在往返過程中是否維護其視圖狀態。如果父控件不維護其視圖狀態,則自動不維護其子控件的視圖狀態 |
| templatecontrol | templatecontrol | 獲取或設置對包含該控件的模板的引用。(asp.net 2.0新增) |
| uniqueid | string | 頁框架給控件分配的分層限定的唯一標識符 |
| clientid | string | 給控件分配的唯一標識符,該唯一標識符在客戶端上呈現為html id特性。clientid與uniqueid是不同的,這是因為uniqueid可以包含冒號字符(:),而在html id特性中該字符無效(并且不允許在客戶端腳本的變量名中使用) |
頁框架
如前面文章所述,如果創建具有ui的自定義服務器控件,則應該從webcontrol或system.web.ui.webcontrols中的任何控件派生,該命名空間為自定義控件提供適當的起點。同樣的道理,讀者應了解一些來自webcontrol類的常見屬性,它們可為控件自動繼承。表2列舉了這些屬性。
| 屬性 | 數據類型 | 說明 |
| backcolor | color | 獲取或設置web服務器控件的背景色。 |
| bordercolor | color | 獲取或設置web控件的邊框顏色。 |
| borderstyle | borderstyle | 獲取或設置web服務器控件的邊框樣式。 |
| borderwidth | unit | 獲取或設置web服務器控件的邊框寬度。 |
| controlstyle | style | 獲取web服務器控件的樣式。 |
| cssclass | string | 獲取或設置由web服務器控件在客戶端呈現的級聯樣式表(css)類。 |
| enabled | bool | 獲取或設置一個值,該值指示是否啟用web服務器控件。 |
| enabletheming | bool | 獲取或設置一個值,該值指示是否對此控件應用主題。(asp.net 2.0新增) |
| font | fontinfo | 獲取與web服務器控件關聯的字體屬性。 |
| forecolor | color | 獲取或設置web服務器控件的前景色(通常是文本顏色)。 |
| height | unit | 服務器控件高度 |
| width | unit | 服務器控件寬度 |
| skinid | string | 獲取或設置要應用于控件的外觀。(asp.net 2.0新增) |
3) 與屬性相關的設計時元數據
創建服務器控件是為了提高應用開發效率,每個控件開發者都希望自己創建出的控件能夠像.net框架中的內置標準服務器控件那樣功能強大且易于使用。例如,當控件應用者在設計界面點擊控件時,可能會希望某些屬性能夠高亮顯示,某些屬性能夠顯示在屬性瀏覽器中等等。如何才能使控件具有這樣的功能呢?這就需要在代碼中加入相關的設計時支持代碼。
實際上,實現設計時元數據是一個比較復雜的內容。然而,作為初學者而言,我們沒有必要掌握得過于深入,下面筆者只講解一些常見的與屬性相關的設計時元數據設置。如下所示代碼,列舉了一些與屬性相關的設計時元數據設置和簡要說明。
· bindable
這個特性表示屬性是否可以綁定一個有效數據源。通常使用布爾值進行設置,例如:bindable(true)。如果使用值true標記屬性,表示該屬性可以綁定一個有效數據源,且應引發該屬性的屬性更改通知;如果屬性值為false,則表示該屬性不能綁定數據。
· browsable
指定屬性是否應該在屬性瀏覽器中顯示,使用布爾值設置。通常情況下,公用屬性和那些希望在屬性瀏覽器中顯示的屬性被設置為browsable(true),只讀屬性和那些不希望在屬性瀏覽器中見到的屬性被設置為browsable(false)。
· category
指定屬性在屬性瀏覽器中進行分組顯示的類別。該設計時特性幫助可視化編輯器將屬性進行邏輯分組。通常分為:外觀(appearance)、行為(behavior)、布局(layout)、數據(data)、操作(action)、鍵盤(key)、鼠標(mouse)等。除此之外,讀者還可以自定義分類,例如category("itemstyle"),表示該屬性在屬性瀏覽器中顯示為itemstyle一組。
· description
指定顯示在屬性瀏覽器下方,屬性的文字說明。例如:description("this is a property")。
以上內容是實現屬性過程中最為常見的設計時元數據設置。無論對于簡單屬性,還是復雜屬性都應該根據需要設置。
· designerserializationvisibility
指定屬性是否以及如何在代碼中序列化,其值為designerserializationvisibility的枚舉值。存在三種設置方式:
(1)designerserializationvisibility(designerserializationvisibility.hidden),指定序列化程序不應該序列化屬性的值;
(2)designerserializationvisibility(designerserializationvisibility.visible),指定應該允許序列化程序序列化屬性的值;
(3)designerserializationvisibility(designerserializationvisibility.content),指定序列化程序應該序列化屬性的內容,而不是屬性本身。此字段為只讀。需要注意的是:沒有designerserializationvisibility特性的成員將被視為具有值為designerserializationvisibility.visible的designerserializationvisibility特性。如果可能,序列化程序會將標記為visible的屬性值序列化為該類型。
· notifyparentproperty
指示當此特性應用到的屬性的值被修改時將通知其父屬性。換言之,如果屬性的父屬性應該在該屬性值更改時接到通知,則向該屬性應用notifyparentproperty特性。通常使用布爾值進行設置。例如,size屬性具有兩個嵌套的子屬性:width和height。那么屬性width和height就應標記為notifyparentpropertyattribute(true),以便當屬性值更改時,它們可以通知父屬性來更新其值并顯示。
· parsechildren
使用該特性指示當在頁上以聲明方式使用控件時,嵌套在服務器控件標記內的xml元素是應該視為屬性還是應視為子控件。通常情況下,包含兩種聲明方式:(1)parsechildren(true),表示將子xml元素作為服務器控件的屬性分析,parsechildren(false),表示將子xml元素作為服務器控件的子控件分析;(2)parsechildren(bool childrenasproperty , string defaultproperty),其中childrenaspropety和方式1中的布爾值參數意義相同,defaultproperty定義默認情況下將子控件分析為的服務器控件的集合屬性。
· persistchildren
該特性指示設計時是否應將服務器控件的子控件作為內部嵌套控件保持。如果該特性為persistchildren(true),則將服務器控件的子控件作為嵌套服務器控件標記保持。如果為persistchildren(false),則將該控件的屬性作為嵌套元素保持。
· persistencemode
指定如何將服務器控件屬性或事件保持到asp.net頁的元數據屬性。共存在4種枚舉設置方式:
(1)persistencemode(persistencemode.attribute),指定屬性或事件保持為特性;
(2)persistencemode(persistencemode.encodedinnerdefaultproperty),指定屬性作為服務器控件的唯一內部文本而。屬性值是html編碼的。只能對字符串做這種指定;
(3)persistencemode(persistencemode.innerdefaultproperty),指定屬性在服務器控件中保持為內部文本。還指示將該屬性定義為元素的默認屬性。只能指定一個屬性為默認屬性;
(4)persistencemode(persistencemode.innerproperty),指定屬性在服務器控件中保持為嵌套標記。這通常用于復雜對象;它們具有自己的持久性屬性;
· defaultproperty
指定服務器控件的默認屬性。例如:[defaultproperty("myproperty")]。
· typeconverter
指定用作此特性所綁定到的對象的轉換器的類型。用于轉換的類必須從typeconverter繼承。使用convertertypename屬性來獲取為該特性所綁定到的對象提供數據轉換的類名。 2. 簡單屬性實現方法
在前面的幾篇文章中已經介紹了一些簡單屬性的實現方法。從中可以發現創建簡單屬性可以使用私有變量、視圖狀態和控件狀態等。在此,筆者無意對這些內容進行重復。感興趣的讀者可參閱有關文章。本節僅對實現簡單屬性的過程進行總結,并通過一個實現簡單枚舉屬性的示例加以說明。示例代碼如下所示:
// 定義枚舉
public enum booktype{
notdefined = 0, fiction = 1, nonfiction = 2
}
// 實現屬性booktype[bindable(true),category("appearance"),defaultvalue(booktype.notdefined),description("fiction or not"),]
public virtual booktype booktype{
get {
object t = viewstate["booktype"];
return (t == null) ? booktype.notdefined : (booktype)t;
}
set { viewstate["booktype"] = value; }
}
以上代碼實現了一個枚舉booktype(包括3個枚舉值)和一個類型為booktype的屬性booktype。根據前文所述基本概念可知,booktype是一個簡單屬性。同時,該屬性將屬性值存儲在視圖狀態viewstate中。通過這個實例,我們基本可以總結出簡單屬性的實現方法:
(1)判斷所要聲明的屬性是否是通用形式屬性;
(2)判斷所要聲明的屬性所封裝的屬性值是否是簡單數值類型、string還是枚舉類型等;
(3)如果步驟1和2都為真,則判定所要聲明的屬性是簡單屬性;
(4)聲明該屬性的設計時特性;
(5)根據屬性的設計需求,編寫讀寫訪問器代碼;
3. 小結
本文介紹了利用asp.net 2.0技術,為自定義服務器控件創建簡單屬性的內容。隨著讀者對自定義服務器控件開發的逐步理解將會發現,實現簡單屬性是構建控件過程中較為簡單,也是較為常見的實現內容。在創建過程中,讀者必須了解使用私有變量、控件狀態和視圖狀態的不同之處。這樣才能又快又好的實現簡單屬性。