在上一篇文章中,曾經提及“複雜屬性”的概念。複雜屬性的最大特徵是屬性的類型是本身具有屬性(稱為子屬性)的類。通常情況下,複雜屬性工作表現為3種形式:連字號形式屬性、內部嵌套形式屬性和內部嵌套形式預設屬性。本文將介紹以上3種形式複雜屬性的具體實現方法。
1. 實現連字號形式複雜屬性
連字號形式屬性是比較常見的複雜屬性。我們常用的Font屬性就是一個複雜屬性,其包括多個子屬性,如Bold、Name等。這種類型屬性具有兩種文法格式:一種是利用連字號文法,在控制項的開始標記中儲存子屬性,例如,Font-Bold,Font-Name。另外一種格式是在控制項的標記中儲存子屬性,例如,<font Bold="true" />。後者比前者的可讀性強。
實現連字號形式的複雜屬性,必須對該複雜屬性及其子屬性實現設定指定的設計時中繼資料。下面首先列舉了複雜屬性實現過程中的相關中繼資料設定樣本。請閱讀下面的原始碼。
public class CustomerControl:WebControl{
[ DesignerSerializationVisibility( DesignerSerializationVisibility.Content), NotifyParentProperty(true) ]
public SizeInfo Size { ...... }
}
如上代碼所示,Size是一個複雜屬性,其屬性類型為SizeInfo(自訂類)。在Size屬性實現前設定了兩個設計時中繼資料:DesignerSerializationVisibility和NotifyParentProperty。DesignerSerializationVisibility用於指定在設計時序列化組件上的屬性時,所使用的持久性類型。其值設定為DesignerSerializationVisibility.Content枚舉值,用於指定序列化程式應該序列化屬性的內容即子屬性,而不是Size屬性本身,因為序列化Size沒有任何意義。另外,還包括一個NotifyParentProperty(true)設定,它的作用是使得屬性瀏覽器中對子屬性的修改通知一直上傳到物件模型,並在被修改了子屬性的控制項中產生修改通知。
在完成了複雜屬性的中繼資料設定後,開發人員還必須對子屬性設定相關的設計時中繼資料。例如,Size包括兩個子屬性Height和Width,那麼它們的實現代碼應如下所示。
[TypeConverter(typeof(ExpandableObjectConverter))]public class SizeInfo{
[ NotifyParentProperty(true) ]
public UInt32 Height {......}
[ NotifyParentProperty(true) ]
public UInt32 Width {......}
}
如上代碼所示,子屬性Height和Width分別被設定了中繼資料NotifyParentProperty(true)。這樣,當子屬性發生修改時,.NET架構將自動產生修改通知,並且通知到父屬性Size。另外,還有一個設計時特性TypeConverter(typeof(ExpandableObjectConverter)),它告訴屬性瀏覽器提供擴充和摺疊樣式,這樣控制項開發人員可以在屬性瀏覽器中直接編輯子屬性。
以上介紹了聲明連字號形式屬性的方法。從中可以看出,在聲明連字號屬性過程中需要把握兩個要點:一是複雜屬性的設計時中繼資料設定;二是子屬性的設計時特性設定。
2. 實現內部嵌套形式複雜屬性
通常情況下,開發人員多實現連字號形式的複雜屬性。然而,對複雜屬性還可以實現內部嵌套的形式。下面的代碼就是一個典型的內部潛逃形式複雜屬性的應用。
<MyControl:CustomeControl id="demo1" runat="server">
<HeaderStyle ForeColor="#FFFF00" BackColor="#99ff00">
</HeaderStyle>
... ...
</MyControl: CustomeControl>
如上代碼所示,自訂控制項MyControl的屬性HeaderStyle是一個典型的內部嵌套形式屬性。實現這種形式的屬性與實現連字號形式屬性有很大不同,需要分為兩種情況。
如果自訂伺服器控制項類繼承自Control類,那麼必須在控制項類之前設定中繼資料屬性ParseChildren和PersistChildren。示意性代碼如下所示。
[ParseChildren(true),PersistChildren(false)]
public class CustomeControl:Control{ ......}
如上代碼所示,在控制項類前設定了兩個中繼資料屬性ParseChildren和PersistChildren。前者用於告知頁面分析器把控制項標記中的內容解析為屬性還是子控制項,該屬性值設定為true,則表示解析為屬性。後者用於告知設計器把控制項標記中的內容儲存為屬性還是子控制項,該屬性值設定為false,表示儲存為屬性。
如果自訂控制項類繼承自WebControl類,那麼就不需要以上的中繼資料屬性設定,因為,WebControl類已經應用了這些中繼資料屬性了。
無論自訂控制項類繼承自WebControl類還是Control類,為實現內部嵌套形式複雜屬性,都必須在屬性實現中設定如下中繼資料屬性。
[ DesignerSerializationVisibility( DesignerSerializationVisibility.Content), NotifyParentProperty(true), PersistenceMode(PersistenceMode.InnerProperty)]
public TableItemStyle HeaderStyle{......}
由上面的代碼可以看到,必須在實現複雜屬性之前,應用3個中繼資料屬性:DesignerSerializationVisibility、NotifyParentProperty和PersistenceMode。前兩個中繼資料屬性在前文中已經說明,第三個PersistenceMode用於指定如何將伺服器控制項屬性或事件保持到ASP.NET頁的中繼資料屬性,該特性的值設定為枚舉值:PersistenceMode.InnerProperty,這表示將所識別屬性(HeaderStyle)保持為嵌套標記。
以上介紹了內部嵌套形式屬性聲明的方法。總結起來分為兩種情況:一是所開發控制項從Control派生,則需要設定五個設計時特性ParseChildrenAttribute(true)、PersistChildren(false)、DesignerSerializationVisibility、NotifyParentProperty和PersistenceMode。前兩個特性在控制項類前設定,用於告訴編譯器將控制項標記內的內容為屬性,需要解析為屬性;後三個特性在屬性前指定,用於指示編譯器此屬性為內部嵌套形式屬性,在應用控制項屬性時,必須採用嵌套形式。二是所開發控制項從WebControl派生,這種情況比較簡單,只需設定上文中後3個設計時特性即可。
3. 實現內部嵌套形式預設複雜屬性
內部嵌套形式預設屬性與內部嵌套形式屬性非常類似,它通常用於設定某個控制項的集合屬性。例如,標準伺服器控制項中的DataList、DropDownList控制項中的屬性均為內部嵌套形式預設屬性。
為了實現這種形式的屬性,主要需設定兩個中繼資料屬性:一是在控制項類前設定ParseChildren(true, "DefaultPropertyName"),指定該控制項中嵌套的標記表示屬性,而非子控制項,同時將嵌套屬性分析為該控制項的集合屬性;二是在集合屬性前設定特性PersistenceMode(PersistenceMode.InnerDefaultProperty),表示將該屬性定義為控制項的預設屬性。
4. 小結
本文介紹了建立複雜屬性的實現方法。這是實現自訂伺服器控制項過程中的重點和痛點內容。在隨後的一篇文章中,我們將通過樣本來加深對複雜屬性實現方法的認識。