XAML 標記延伸概觀
標記延伸是一項用於取得值的 XAML 技術,這個值既不是基本型別,也不是特定 XAML 型別。 對於屬性使用方式,標記延伸會使用以左大括號 { 開始標記延伸範圍、以右大括號 } 結束範圍的已知字元序列。 使用 .NET Framework XAML 服務時,您可以使用某些來自 System.Xaml 組件的預先定義 XAML 語言標記延伸。 您也可以從 System.Xaml 中定義的 MarkupExtension 類別建立子類別,並定義您自己的標記延伸。 或者,如果您已參考該架構,您也可以使用該架構所定義的標記延伸。
存取標記延伸的使用方式時,XAML 物件寫入器可以透過 MarkupExtension.ProvideValue 覆寫中的服務連接點,提供服務給自訂 MarkupExtension 類別。 這些服務可用以了解關於使用方式、物件寫入器的特定功能、XAML 結構描述內容等背景資訊。
這個主題包含下列章節。
- XAML 定義標記延伸
- MarkupExtension 基底類別
- 定義自訂標記延伸的支援型別
- 自訂標記延伸適用的建構函式模式和位置引數
- 自訂標記延伸適用的具名引數
- 從標記延伸實作存取服務提供者內容
- 標記延伸的屬性項目用法
- 自訂標記延伸的屬性設定
- 標記延伸使用方式的序列化
- XAML 節點資料流中的標記延伸
- 相關主題
XAML 定義標記延伸
.NET Framework XAML 服務實作了數種標記延伸以支援 XAML 語言。 這些標記延伸會對應至 XAML 語言規格的各部分。 這些標記延伸通常可透過一般常見語法中的 x: 前置字元加以識別, 這些 XAML 語言項目的 .NET Framework XAML 服務實作全部衍生自 MarkupExtension 基底類別。
注意事項 |
---|
x: 前置詞可在 XAML 產物的根項目中,用於 XAML 語言命名空間的一般 XAML 命名空間對應。例如,各種特定架構所適用的 Visual Studio 專案與頁面範本,即會使用此 x: 對應來啟始 XAML 檔案。您可以在自己的 XAML 命名空間對應中選擇不同的前置詞語彙基元,但是本文件會以預設的 x: 對應,識別 XAML 語言 XAML 命名空間中已定義的實體部分,而不是特定架構的預設 XAML 命名空間或其他任意 CLR 或 XML 命名空間。 |
x:Type
x:Type 會提供具名型別的 Type 物件。 此功能最常用在以基礎 CLR 型別與型別衍生做為群組 Moniker 或識別項的延後機制中。 WPF 樣式與範本及其 TargetType 屬性用法,即為明確的範例。 如需詳細資訊,請參閱 x:Type 標記延伸。
x:Static
x:Static 會從實值型別程式碼實體產生靜態值,而這些實體本身不是屬性值型別,但卻可評估為該型別。 這在指定型別定義中眾所皆知的常數值時會很有用。 如需詳細資訊,請參閱 x:Static 標記延伸。
x:Null
x:Null 會將 null 指定為 XAML 成員的值。 根據特定型別的設計或大型架構概念,null 不見得一定是屬性的預設值或空字串屬性的隱含值。 如需詳細資訊,請參閱 x:Null 標記延伸。
x:Array
x:Array 可在刻意不使用基底項目和控制項模型所提供的集合支援時,支援以 XAML 語法建立一般陣列。 如需詳細資訊,請參閱 x:Array 標記延伸。 特別是在 XAML 2009 中,會以語言基本型別 (而不是延伸) 的形式來存取陣列。 如需詳細資訊,請參閱 XAML 2009 語言功能。
x:Reference
x:Reference 是 XAML 2009 的一部分,延伸自原始 (2006) 語言集。 x:Reference 表示對物件圖形中其他現有物件的參考。 該物件是由其 x:Name 來識別。 如需詳細資訊,請參閱 x:Reference 標記延伸。
其他 x: 建構
另外還有其他支援 XAML 語言功能的 x: 建構存在,但是這些建構不是以標記延伸的形式實作。 如需詳細資訊,請參閱 XAML 命名空間 (x:) 語言功能。
MarkupExtension 基底類別
若要定義可以與 System.Xaml 中預設的 XAML 讀取器和 XAML 寫入器實作進行互動的自訂標記延伸,您可以從抽象 MarkupExtension 類別衍生類別。 該類別具有一個要覆寫的方法,即 ProvideValue。 您可能還必須定義其他建構函式,以支援使用標記延伸時所需的引數,以及對應至可設定的屬性。
自訂標記延伸可以透過 ProvideValue 存取服務內容,所謂的服務內容是指 XAML 處理器實際上用以叫用標記延伸的環境。 在載入路徑中,這通常是 XamlObjectWriter。 在儲存路徑中,這通常是 XamlXmlWriter。 這兩者都會報告服務內容,做為會實作服務提供者模式的內部 XAML 形式。 如需可用服務和其所表示項目的詳細資訊,請參閱 XAML 的型別轉換子和標記延伸。
您的標記延伸類別必須使用公用存取層級;XAML 處理器必須永遠能夠執行個體化標記延伸的支援類別,以便使用其服務。
定義自訂標記延伸的支援型別
在使用 .NET Framework XAML 服務或建置於 .NET Framework XAML 服務的架構時,您可以透過兩種方式來命名標記延伸支援型別。 型別名稱與 XAML 物件寫入器在 XAML 中發現標記延伸用法時,嘗試存取及叫用標記延伸支援型別的方式有關。 使用下列其中一種命名策略:
將型別名稱命名為完全符合 XAML 標記用法語彙基元的名稱。 例如,若要支援 {Collate ...} 延伸用法,請將支援型別命名為 Collate。
將型別名稱命名為用法字串語彙基元,再加上後置字元 Extension。 例如,若要支援 {Collate ...} 延伸用法,請將支援型別命名為 CollateExtension。
查閱的順序是先尋找具有 Extension 後置字元的類別名稱,再尋找不具 Extension 後置字元的類別名稱。
就標記用法的觀點而言,在用法中加入 Extension 後置字元是有效的。 但如此一來,Extension 就會真的變成類別名稱的一部分,而在支援類別不具 Extension 後置字元時,XAML 物件寫入器將無法解析該用法的標記延伸支援類別。
預設建構函式
對於所有的標記延伸支援型別,您均應公開公用預設建構函式。 只要 XAML 物件寫入器是從物件項目用法執行個體化標記延伸,就必須有預設建構函式。 對於標記延伸,通常會有支援物件項目用法,特別是在序列化的時候。 但如果您只想要支援標記延伸的屬性用法,則可以實作不含公用建構函式的標記延伸。
如果您所使用的標記延伸不含引數,則必須使用預設建構函式加以支援。
自訂標記延伸適用的建構函式模式和位置引數
對於預定要採引數使用方式的標記延伸,公用建構函式必須對應至預定使用方式的模式。 換句話說,如果標記延伸依設計要有一個位置引數做為有效使用方式,您即應該以一個接受位置引數的輸入參數來支援公用建構函式。
例如,假設 Collate 標記延伸預定只支援某個模式,這個模式以一個位置引數表示以 CollationMode 列舉常數指定的模式。 在這種情況下,應該要有形式如下的建構函式:
public Collate(CollationMode collationMode) {...}
在基本層級,傳遞給標記延伸的引數是字串,因為這些引數會從標記的屬性值轉送出去。 您可以讓所有的引數都是字串,並在該層級處理輸入。 但是,您可以在將標記延伸引數傳遞給支援類別之前,執行特定處理。
處理方式在概念上會將標記延伸當成要建立的物件,然後設定其成員值。 每個要設定的指定屬性,都會比照 XAML 進行剖析時可對已建立的物件設定指定成員的方式,進行評估。 但是有兩個重大差異:
如前所述,標記延伸支援型別不需要預設建構函式,即可在 XAML 中執行個體化。 其物件建構作業會延後到其位於文字語法中的可能引數已語彙基元化並已評估為位置或具名引數,且於當時呼叫適當的建構函式之後,才會執行。
標記延伸用法可以是巢狀結構。 最內層的標記延伸會最先受到評估。 因此,您可以採用這類使用方式,並將其中一個建構參數宣告成需要有值轉換子 (例如標記延伸) 才能產生的型別。
前述範例說明了對這類處理的依賴性。 .NET Framework XAML 服務 XAML 物件寫入器會將列舉常數名稱處理成原生層級的列舉值。
在處理標記延伸位置參數的文字語法時,也可能會依賴與建構引數中的型別相關聯的型別轉換子。
這些引數稱為位置引數,因為使用這些引數時,語彙基元的出現順序會對應於被指派這些語彙基元之建構函式參數的位置順序。 以下列建構函式簽章為例:
public Collate(CollationMode collationMode, object collateThis) {...}
XAML 處理器會預期這個標記延伸應有兩個位置引數。 如果有使用方式 {Collate AlphaUp,{x:Reference circularFile}},則 AlphaUp 語彙基元會傳送至第一個參數並被評估為 CollationMode 列舉具名常數。 內部 x:Reference 的結果會傳送至第二個參數並被評估為物件。
在 XAML 針對標記延伸語法和處理指定的規則中,會以逗號做為引數之間的分隔符號,無論引數是位置引數還是具名引數,都是如此。
位置引數的重複 Arity
如果 XAML 物件寫入器發現具有位置引數的標記延伸用法,並且有多個建構函式引數採用該數目的引數 (重複 Arity),並不見得表示有錯誤發生。 此行為取決於可自訂的 XAML 結構描述內容設定 SupportMarkupExtensionsWithDuplicateArity。 如果 SupportMarkupExtensionsWithDuplicateArity 為 true,則 XAML 物件寫入器不應僅因為重複 Arity 而擲回例外狀況。 此範圍以外的行為並未嚴格定義。 基本設計假設是,結構描述內容具有可用於特定參數的型別資訊,並且可嘗試符合重複候選項目的明確轉型,以查看哪個簽章最為相符。 如果沒有任何簽章可通過執行於 XAML 物件寫入器上的特定結構描述內容所制定的測試,則仍可能會擲回例外狀況。
根據預設,在 .NET Framework XAML 服務的 CLR 型 XamlSchemaContext 中,SupportMarkupExtensionsWithDuplicateArity 會是 false。 因此,預設 XamlObjectWriter 如果發現在支援型別的建構函式中含有重複 Arity 的標記延伸用法,將會擲回例外狀況。
自訂標記延伸適用的具名引數
在使用時,XAML 所指定的標記延伸也可使用具名引數格式。 在語彙基元化的第一個層級,文字語法會分成數個引數。 任何引數內若出現等號 (=),即表示該引數是具名引數。 這類引數也會語彙基元化成名稱/值組。 名稱在這種情況下命名的是標記延伸之支援型別的公用可設定屬性。 如果您要支援具名引數使用方式,則應提供這些公用可設定屬性。 只要這些屬性仍是公用的,即可以是繼承的屬性。
從標記延伸實作存取服務提供者內容
所有值轉換子可用的服務都相同。 差別在於每個值轉換子接收服務內容的方式。 服務存取方式和可用服務的相關說明,列載於 XAML 的型別轉換子和標記延伸主題中。
標記延伸的屬性項目用法
標記延伸用法的情節,常是根據屬性用法中的標記延伸使用方式而設計的。 但支援類別仍有可能經定義而支援屬性項目用法。
若要支援標記延伸的屬性項目用法,請定義公用預設建構函式。 此應為執行個體建構函式,而不是靜態建構函式。 之所以需要此建構函式,是因為 XAML 處理器通常必須對任何從標記處理的物件項目叫用預設建構函式,而這會加入標記延伸類別做為物件項目。 在進階的情節中,您可以為類別定義非預設的建構路徑 (如需詳細資訊,請參閱 x:FactoryMethod 指示詞)。但您不應將這些模式用於標記延伸用法,因為這會讓用法模式的探索變得困難得多;對未經處理的標記的設計人員與使用者而言,都是如此。
自訂標記延伸的屬性設定
若要支援設計環境與特定的 XAML 物件寫入器情節,您應為具有數個 CLR 屬性的標記延伸支援型別設定屬性。 這些屬性會報告預定的標記延伸用法。
MarkupExtensionReturnTypeAttribute 會針對 ProvideValue 所傳回的物件型別,報告 Type 資訊。 ProvideValue 會根據其純正簽章,傳回 Object。 但消費者不盡相同,有些可能需要更精確的傳回型別資訊。 它們包括:
設計工具與 IDE,它們可能可以提供標記延伸用法的型別感知支援。
SetMarkupExtension 處理常式在目標類別上的進階實作,此類實作可能需倚賴反映來判斷標記延伸的傳回型別,而非依名稱對特定的已知 MarkupExtension 實作進行分支。
標記延伸使用方式的序列化
XAML 物件寫入器在處理標記延伸用法並且呼叫 ProvideValue 時,先前以標記延伸用法形式存在的內容仍會保留在 XAML 節點資料流中,但不會在物件圖形中。 物件圖形中只會保留值。 如果您是因為設計情節,或是其他原因而必須將原始標記延伸使用方式保留到序列化輸出中,您必須設計自己的基礎結構,以從載入路徑 XAML 節點資料流追蹤標記延伸使用方式。 您可以實作行為以從載入路徑重新建立節點資料流的項目,並將其播放回 XAML 寫入器以進行儲存路徑中的序列化,而取代掉節點資料流適當位置中的值。
XAML 節點資料流中的標記延伸
您在使用載入路徑上的 XAML 節點資料流時,標記延伸用法會顯示為節點資料流中的物件。
如果標記延伸用法使用位置引數,該標記延伸用法將會顯示為具有初始設定值的起始物件。 節點資料流的粗略文字顯示如下:
StartObject (XamlType 是標記延伸的定義型別,而非其傳回型別)
StartMember (XamlMember 的名稱為 _InitializationText)
Value (值為字串形式的位置引數,其中包含分隔符號)
EndMember
EndObject
具有具名引數的標記延伸用法會顯示為具有使用相關名稱之成員的物件,每個物件都會以文字字串值設定。
若要實際叫用標記延伸的 ProvideValue 實作,必須進行型別對應及建立標記延伸支援型別執行個體,因此必須有 XAML 結構描述內容。 這就是標記延伸用法以此方式保存在預設 .NET Framework XAML 服務節點資料流中的原因之一 - 載入路徑的讀取器部分通常沒有必要的 XAML 結構描述內容可以使用。
您在使用儲存路徑上的 XAML 節點資料流時,物件圖形表示上通常不會顯示任何項目通知您要序列化的物件最初是由標記延伸用法和 ProvideValue 結果所提供。 如需保留標記延伸用法以進行反覆存取,同時擷取物件圖形中的其他變更,您必須設計出適用於這些情況的獨立技術,從原始 XAML 輸入保存標記延伸用法的知識。 例如,若要還原標記延伸用法,您可能必須使用儲存路徑上的節點資料流以還原標記延伸用法,或在原始 XAML 與反覆存取的 XAML 之間執行某種型別的合併。 有些 XAML 實作架構 (如 WPF) 會使用中繼型別 (運算式) 協助呈現標記延伸用法提供值的案例。