XAML 和自訂類別
更新:2007 年 11 月
可延伸標記語言 (XAML) 支援以任何 Common Language Runtime (CLR) 語言定義自訂類別或結構,然後再使用 XAML 標記 (Markup) 存取該類別,包括在同一標記檔案中混合使用 Windows Presentation Foundation (WPF) 定義的 XAML 和自訂類別的 XAML 標記 (Tag)。本主題將說明自訂類別必須符合哪些要求才能當做 XAML 項目使用。
這個主題包含下列章節。
- 應用程式或組件中的自訂類別
- 自訂類別當做 XAML 項目使用的要求
- 自訂類別的屬性 (Property) 當做 XAML 屬性 (Attribute) 使用的要求
- 自訂類別事件上的 XAML 事件處理常式屬性語法的要求
- 撰寫集合屬性
- 宣告 XAML 內容屬性
- 序列化 XAML
- 相關主題
應用程式或組件中的自訂類別
XAML 中使用的自訂類別可利用兩種方式加以定義:在程式碼後置 (Code-Behind) 或其他產生主要 Windows Presentation Foundation (WPF) 應用程式的程式碼中定義,或者定義為個別組件 (例如做為類別庫使用的可執行檔或 DLL) 中的類別。這兩種方式各有其優缺點。
建立類別庫的好處是不同應用程式可共用自訂類別。另外個別的類別庫也有助於減少應用程式的版本控制問題,並能簡化建立要當做 XAML 頁面上根項目之類別的步驟。
在應用程式中定義自訂類別的好處是,這種方式相對精簡,可減少在主要可執行檔以外加入個別組件時遇到的部署和測試問題。不過有個明顯的缺點是,您無法將相同組件中定義的類別做為 XAML 頁面的根項目。
不論是定義在相同或不同的組件中,自訂類別都必須在 CLR 命名空間和 XML 命名空間之間進行對應,才能當做項目用於 XAML 中。請參閱 XAML 命名空間和命名空間對應。
自訂類別當做 XAML 項目使用的要求
要能夠具現化為物件項目,類別必須符合下列要求:
自訂類別必須為公用並支援預設的 (無參數) 公用建構函式 (Managed 程式碼結構隱含支援這種建構函式)。
您的自訂類別不能是巢狀類別 (巢狀類別及其語法中的「點號」 (Dot) 會妨礙其他 WPF 功能,例如附加屬性)。
除了啟用物件項目語法,針對接受該物件做為實值型別的任何其他公用屬性,也必須啟用屬性項目語法。這是因為物件現在可具現化為物件項目,並可填入這種屬性的屬性項目值。
結構
您定義為自訂型別的結構,永遠都能在 WPF 中以 XAML 建構。這是因為 CLR 編譯器 (Compiler) 會針對結構隱含性建立預設的建構函式 (Constructor),將所有屬性值初始化為預設值。在某些情況中,您可能不想要結構的預設建構行為和/或物件項目使用方式。這可能是因為在概念上,結構原意是要將值和函式以等位方式填入,其中包含的值可能有互斥 (Mutually Exclusive) 解譯,因此其中沒有可設定的屬性。GridLength 是一個這類型結構的 WPF 範例。一般而言,這類型的結構應該實作型別轉換子 (Type Converter),以使用為結構的值建立不同解譯或模式的字串慣例,讓值能夠以屬性 (Attribute) 表單表達,而結構也應該透過非預設的建構函式,公開程式碼建構的類似行為。
自訂類別的屬性 (Property) 當做 XAML 屬性 (Attribute) 使用的要求
屬性必須參考傳值 (By-Value) 型別 (例如基本型別),或使用型別在類別層級有預設建構函式或專屬型別轉換子的類別。
或者,屬性可參考抽象類別型別或介面。針對抽象類別或介面,在執行階段的屬性值必須填入實作介面的實體類別執行個體,或衍生自抽象類別的類別執行個體。
屬性 (Property) 可在抽象類別上宣告,但只能設定於衍生自抽象類別的實體類別上,因為只要建立類別的物件項目,都需要類別上的公用預設建構函式。
啟用型別轉換子的屬性語法
如果您在類別層級提供專用的且使用屬性 (Attribute) 的型別轉換子,套用的型別轉換可供需要具現化該型別的任何屬性 (Property) 啟用屬性 (Attribute) 語法。型別轉換子不會啟用型別的物件項目使用方式;唯該型別有預設建構函式存在時,才能啟用物件項目的使用方式。因此,一般而言,啟用型別轉換子的屬性 (Property) 不能在屬性 (Property) 語法中使用,除非型別本身也支援物件項目語法。例外的是,您可以指定屬性 (Property) 項目語法,但屬性 (Property) 項目要包含字串。這種使用方式基本上相當於屬性 (Attribute) 語法使用方式,且並不常見,除非需要以更嚴格的方式處理屬性 (Attribute) 值的空白區。例如,以下是接受字串的屬性 (Property) 項目使用方式,以及對等的屬性 (Attribute) 使用方式:
<Button>Hallo!
<Button.Language>
de-DE
</Button.Language>
</Button>
<Button Language="de-DE">Hallo!</Button>
各種接受 Cursor 型別的屬性 (Property),就是允許屬性 (Attribute) 語法,但透過 XAML 不允許包含物件項目之屬性 (Property) 項目語法的屬性 (Property) 的範例。Cursor 類別有專用型別轉換子 CursorConverter,但不會公開預設建構函式,因此 Cursor 屬性 (Property) 只能透過屬性 (Attribute) 語法設定,即使實際 Cursor 型別為參考型別也一樣。
個別屬性的型別轉換子
此外,屬性本身可以在屬性層級宣告型別轉換子。這可提供「迷你語言」 (Mini Language),以具現化屬性 (Property) 的型別物件,方式是根據適當的型別,將屬性 (Attribute) 的傳入字串值處理成 ConvertFrom 作業的輸入。一般這麼做是要提供方便的存取子,並非做為能在 XAML 中設定屬性 (Property) 的唯一方式。不過,若屬性 (Attribute) 要使用不提供預設建構函式或屬性型別轉換子的現有 CLR 型別,您也可以使用型別轉換子。在 WPF API 中的例子,則包括接受 CultureInfo 型別的某些屬性 (Property)。在此例中,WPF 使用了現有的 Microsoft .NET Framework CultureInfo 型別加強與舊版 Framework 的相容性和移轉案例,但 CultureInfo 型別不支援將必要的建構函式或型別層級的型別轉換,直接當做 XAML 屬性 (Property) 值使用。
每當您公開有 XAML 使用方式的屬性 (Property) 時,尤其如果您是控制項作者時,則應認真考慮使用相依性屬性 (Property) 支援該屬性 (Property)。這在使用 XAML 處理器的現有 Windows Presentation Foundation (WPF) 實作時尤然,因為您可以使用 DependencyProperty 支援增進效能。相依性屬性會公開屬性的屬性系統功能,這些功能是使用者預期 XAML 可存取屬性會具備的功能。這包括如動畫、資料繫結和樣式支援等功能。如需詳細資訊,請參閱自訂相依性屬性和 XAML 載入和相依性屬性。
撰寫型別轉換子並設定其屬性
您經常需要撰寫自訂 TypeConverter 衍生類別,以便為屬性型別提供型別轉換。如需如何建立支援 XAML 使用方式的型別轉換子、如何從其進行衍生作業,以及如何套用 TypeConverterAttribute 的指示,請參閱 TypeConverter 和 XAML。
自訂類別事件上的 XAML 事件處理常式屬性語法的要求
若要能當做 CLR 事件使用,則此事件必須在支援預設建構函式的類別上公開為公用事件,或在抽象類別上公開為公用事件。該抽象類別的事件可讓衍生類別存取。為方便當做路由事件使用,CLR 事件應實作明確的 add 和 remove 方法。這兩個方法會加入和移除 CLR 事件簽章的處理常式,並將這些處理常式轉送到 AddHandler 和 RemoveHandler 方法。這些方法會將處理常式加入到事件附加所在的執行個體上的路由事件處理常式存放區,或從存放區移除事件處理常式。
注意事項: |
---|
您可以使用 AddHandler 直接註冊路由事件的處理常式,並刻意不定義公開路由事件的 CLR 事件。一般並不建議這麼做,因為事件將不會啟用所附加之處理常式的 XAML 屬性 (Attribute) 語法,產生的類別將提供透明度較低的類別物件模型 XAML 檢視。 |
撰寫集合屬性
接受集合型別的屬性 (Property) 有 XAML 語法,可讓您指定要加入到集合的物件。此語法有兩個值得注意的功能。
屬於集合物件的物件不需要在物件項目語法中指定。每當您在 XAML 中指定接受集合型別的屬性時,集合型別的存在是隱含的。
集合屬性的子項目會處理成為集合的成員。一般而言,以程式碼存取集合成員是透過集合方法 (例如 Add) 或透過集合索引子屬性執行。但 XAML 語法不支援方法或索引子。集合很明顯是建立項目樹狀結構的基本要求,您需要想辦法在宣告式 XAML 中填入這些集合。因此,集合屬性子項目的處理方式是將它們加入到屬於集合屬性型別值的集合。
WPF XAML 處理器會使用下列定義判斷集合屬性的組成項目。屬性的屬性型別必須實作下列其中一項:
實作 IList。
實作 IDictionary 或泛型對應項 (IDictionary<TKey, TValue>)。
衍生自 Array (如需 XAML 中陣列的詳細資訊,請參閱 x:Array 標記延伸)。
實作 IAddChild (WPF 定義的介面)。
每一種型別都有 Add 方法,XAML 處理器會使用此方法將項目加入基礎集合中。
注意事項: |
---|
WPF XAML 處理器在偵測集合時不支援泛型 List 和 Dictionary 介面 (IList<T> 和 IDictionary<TKey, TValue>)。然而您可以使用 List<T> 類別做為基底類別,因為它直接實作 IList,或是使用 Dictionary<TKey, TValue> 做為基底類別,因為它直接實作 IDictionary。 |
當您宣告接受集合的屬性時,請務必注意屬性值如何在型別的新執行個體中初始化。如果您不將屬性實作為相依性屬性,那麼讓屬性使用會呼叫集合型別建構函式的支援欄位即已足夠。如果屬性是相依性屬性,則可能需要將集合屬性初始化設為預設型別建構函式的一部分。這是因為相依性屬性會從中繼資料取得預設值,而您一般不會想要集合屬性的初始值是靜態的共用集合 (每個包含的型別執行個體都應有一個集合執行個體)。如需詳細資訊,請參閱自訂相依性屬性。
您可以為集合屬性實作自訂集合型別。由於集合屬性會視為隱含,自訂集合型別不需要提供預設建構函式,就能在 XAML 中隱含使用。不過,您可以選擇性提供集合型別的預設建構函式。這可能是值得的做法,因為除非您提供預設建構函式,否則無法將集合明確宣告為物件項目。部分標記作者可能會因為標記樣式的關係,希望看到明確的集合。此外,當您建立使用集合型別做為屬性值的新物件時,預設建構函式也可簡化初始化要求。
宣告 XAML 內容屬性
XAML 語言定義了 XAML 內容屬性 (Property) 的概念。可在物件語法中使用的每個類別都能有一個 XAML 內容屬性。若要將屬性宣告為類別的 XAML 內容屬性,請將 ContentPropertyAttribute 套用為類別定義的一部分。將所要的 XAML 內容屬性 (Property) 的名稱指定為屬性 (Attribute) 中的 Name。
您可以將集合屬性 (Property) 指定為 XAML 內容屬性。如此一來,該屬性的使用方式就變成是物件項目可有一個或多個子項目,中間沒有任何集合物件項目或屬性項目標記。這些項目接著會視為是 XAML 內容屬性的值,並會加入到支援的集合執行個體中。
部分現有 WPF XAML 內容屬性會使用 Object 的屬性型別。這會啟用可接受基本值 (例如 String) 以及接受單一參考物件值的 XAML 內容屬性。如果遵循此模型,您的型別會負責判斷型別以及處理可能的型別。Object 型別模型通常是用來支援將物件當做字串加入 (這會接收預設展示處理) 的簡單用法,或加入指定非預設展示之物件內容的進階用法。
序列化 XAML
在某些情況下,例如您是控制項作者,也可能會想確保可在 XAML 中具現化的任何物件表示,都還是能序列化回對等的 XAML。此序列化作業的要求不在本主題討論之列。請參閱 控制項撰寫概觀 和 項目樹狀結構和序列化。