XAML のマークアップ拡張機能の概要
マークアップ拡張機能は、プリミティブでも特定の XAML 型でもない値を取得するための XAML 手法です。 属性の使用方法では、マークアップ拡張機能は、左中かっこ ({) を開いてマークアップ拡張機能のスコープを入力し、右中かっこ (}) を使用して終了するという、既知の文字シーケンスを使用します。 .NET Framework XAML サービスを使用する場合は、System.Xaml アセンブリから定義済みの XAML 言語マークアップ拡張機能をいくつか使用できます。 また、System.Xaml で定義された MarkupExtension クラスからサブクラスを作成し、独自のマークアップ拡張機能を定義することもできます。 または、特定のフレームワークを既に参照している場合は、そのフレームワークによって定義されたマークアップ拡張機能を使用することができます。
マークアップ拡張機能の使用方法にアクセスすると、XAML オブジェクト ライターは MarkupExtension.ProvideValue オーバーライドのサービス コネクション ポイントを介し、カスタム MarkupExtension クラスにサービスを提供することができます。 サービスを使用すると、使用方法に関するコンテキスト、オブジェクト ライターの特定の機能、XAML スキーマ コンテキストなどを取得することができます。
このトピックは、次のセクションで構成されています。
- XAML で定義されたマークアップ拡張機能
- MarkupExtension 基本クラス
- カスタム マークアップ拡張機能のサポート型の定義
- カスタム マークアップ拡張機能のコンストラクター パターンと位置指定引数
- カスタム マークアップ拡張機能の名前付き引数
- マークアップ拡張機能の実装からのサービス プロバイダー コンテキストへのアクセス
- マークアップ拡張機能のプロパティ要素の使用方法
- カスタム マークアップ拡張機能の属性設定
- マークアップ拡張機能の使用方法のシリアル化
- XAML ノード ストリームのマークアップ拡張機能
- 関連トピック
XAML で定義されたマークアップ拡張機能
XAML 言語をサポートするため、.NET Framework XAML サービスによっていくつかのマークアップ拡張機能が実装されています。 これらのマークアップ拡張機能は、言語としての XAML の仕様の部分に対応しています。 これらは、一般的な使用法の構文内では、通常は x: プレフィックスで識別できます。 これらの XAML 言語要素に対する .NET Framework XAML サービスの実装は、すべて MarkupExtension 基本クラスから派生します。
メモ |
---|
x: プレフィックスは、XAML 稼働環境のルート要素において、XAML 言語の名前空間の一般的な XAML 名前空間の割り当てに使用されます。たとえば、さまざまな特定のフレームワークの Visual Studio プロジェクト テンプレートおよびページ テンプレートは、この x: マッピングを使用して XAML ファイルを実行します。独自の XAML 名前空間の割り当てで別のプレフィックス トークンを選択できますが、このドキュメントでは、特定のフレームワークの既定の XAML 名前空間またはその他の任意の CLR 名前空間や XML 名前空間ではなく、XAML 言語の XAML 名前空間の定義済みの部分であるエンティティを識別する方法として、既定の x: の割り当てを想定しています。 |
x:Type
x:Type は、指定した型の Type オブジェクトを示します。 この機能が最も頻繁に使用される事例としては、グループ モニカーまたは識別子の形式として基になる CLR 型および型派生が使用される遅延機構が挙げられます。 具体例としては、WPF のスタイルおよびテンプレート、その TargetType プロパティの使用方法が挙げられます。 詳細については、「x:Type マークアップ拡張機能」を参照してください。
x:Static
x:Static は、直接的にはプロパティの値の型ではなくても、その型に評価することができる値型コード エンティティから静的な値を生成します。 これは、型定義で既知の定数として既に存在する値を指定するときに便利です。 詳細については、「x:Static のマークアップ拡張機能」を参照してください。
x:Null
x:Null は、XAML メンバーの値として null を指定します。 特定の型の設計やフレームワークの広義の概念によっては、null がプロパティの既定値に該当しない場合や、空の文字列属性の暗黙的な値に該当しない場合もあります。 詳細については、「x:Null のマークアップ拡張機能」を参照してください。
x:Array
x:Array は、XAML 構文での一般的な配列の作成をサポートします。基本要素とコントロール モデルで提供されているコレクションのサポートをあえて使用しない場合に使用します。 詳細については、「x:Array のマークアップ拡張機能」を参照してください。 特に XAML 2009 では、配列は拡張機能としてではなく、言語プリミティブとしてアクセスされます。 詳細については、「XAML 2009 言語機能」を参照してください。
x:Reference
x:Reference は、元の (2006) 言語セットの拡張機能である XAML 2009 の一部です。 x:Reference は、オブジェクト グラフにある別の既存オブジェクトへの参照を表します。 このオブジェクトは、x:Name によって識別されます。 詳細については、「x:Reference のマークアップ拡張機能」を参照してください。
その他の x: 構成要素
XAML 言語機能をサポートするための、その他の x: 構成要素も存在しますが、マークアップ拡張機能としては実装されません。 詳細については、「XAML 名前空間 (x:) 言語機能」を参照してください。
MarkupExtension 基本クラス
System.Xaml の XAML リーダーと XAML ライターの既定の実装と相互作用できるカスタム マークアップ拡張機能を定義するには、抽象クラス MarkupExtension からクラスを派生します。 このクラスには、オーバーライドする必要があるメソッドが 1 つあります (ProvideValue)。 また、マークアップ拡張機能の使用方法の引数をサポートするための追加のコンストラクターと、それに対応する設定可能なプロパティも定義する必要があります。
カスタム マークアップ拡張機能では、ProvideValue を通じて、マークアップ拡張機能が実際に XAML プロセッサによって起動される環境を報告するサービス コンテキストにアクセスします。 読み込みパスでは、これは通常 XamlObjectWriter です。 保存パスでは、これは通常 XamlXmlWriter です。 これらはそれぞれ、サービス プロバイダー パターンを実装する内部 XAML サービス プロバイダー コンテキスト クラスとして、サービス コンテキストを報告します。 使用できるサービスと、これらのサービスが何を表現するかについての詳細については、「XAML の型コンバーターおよびマークアップ拡張機能」を参照してください。
マークアップ拡張機能クラスは、パブリックなアクセス レベルを使用する必要があります。また、XAML プロセッサは、サービスを使用するために、マークアップ拡張機能のサポート クラスを常にインスタンス化する必要があります。
カスタム マークアップ拡張機能のサポート型の定義
.NET Framework XAML サービス、または .NET Framework XAML サービスに基づいて構築されたフレームワークを使用する場合は、2 種類の方法でマークアップ拡張機能のサポート型に名前を付けることができます。 型名は、XAML オブジェクト ライターが XAML でのマークアップ拡張機能の使用を検出したときに、マークアップ拡張機能のサポート型にアクセスして呼び出しを試みる方法に関連しています。 次の名前付け方法のいずれかを使用します。
XAML マークアップの使用方法トークンに対して厳密に一致するよう、型名を付ける。 たとえば、{Collate ...} 拡張機能の使用方法をサポートするには、サポート型を Collate と名前付けします。
使用方法の文字列トークンにサフィックス Extension を付加し、型名を付ける。 たとえば、{Collate ...} 拡張機能の使用方法をサポートするには、サポート型を CollateExtension と名前付けします。
検索は、まずサフィックス Extension が付加されたクラス名、次にサフィックス Extension が付加されていないクラス名という順序で行われます。
マークアップの使用方法の観点からは、サフィックス Extension を使用方法の一部に含めることは有効です。 ただし、この場合、Extension は完全にクラス名の一部として扱われます。したがって、サポート クラスにサフィックス Extension が設定されていない場合、XAML オブジェクト ライターはその使用方法についてマークアップ拡張機能のサポート クラスを解決できません。
既定のコンストラクター
すべてのマークアップ拡張機能のサポート型について、パブリックの既定のコンストラクターを公開する必要があります。 既定のコンストラクターは、XAML オブジェクト ライターがオブジェクト要素の使用方法からマークアップ拡張機能をインスタンス化するすべてのケースで必要です。 マークアップ拡張機能でオブジェクト要素の使用方法をサポートすることは、特にシリアル化については、正当に予期できることと言えます。 ただし、マークアップ拡張機能の属性の使用方法だけをサポートする場合は、パブリック コンストラクターがなくてもマークアップ拡張機能を実装することができます。
マークアップ拡張機能の使用方法に引数がない場合に使用をサポートするには、既定のコンストラクターが必要です。
カスタム マークアップ拡張機能のコンストラクター パターンと位置指定引数
目的の引数の使用方法を持つマークアップ拡張機能については、パブリック コンストラクターは、目的の使用方法のモードに対応する必要があります。 つまり、有効な使用方法として位置指定引数を 1 つ使用するようにマークアップ拡張機能が設計されている場合は、その位置指定引数を取る 1 つの入力パラメーターを使用するパブリック コンストラクターをサポートする必要があります。
たとえば、Collate マークアップ拡張機能が、モードを表す位置指定引数 (CollationMode 列挙定数として指定される) が 1 つしかないモードのみをサポートするとします。 この場合は、次の形式のコンストラクターが必要になります。
public Collate(CollationMode collationMode) {...}
基本的なレベルでは、マークアップ拡張機能に渡される引数はマークアップの属性値から転送されるので、文字列になります。 すべての引数を文字列にし、そのレベルでの入力で作業できます。 ただし、マークアップ拡張機能の引数がサポート クラスに渡される前に行われる、特定の処理へのアクセス許可はあります。
この処理では、マークアップ拡張機能を作成されるオブジェクトと見なして概念的に行われ、その後、メンバー値が設定されます。 設定される特定のプロパティは、XAML が解析されたときに、作成されたオブジェクトで指定のメンバーが設定されるのと同じように評価されます。 ただし、次の 2 つの重要な違いがあります。
前のように、マークアップ拡張機能のサポート型では、XAML でインスタンス化するために既定のコンストラクターを必要としません。 オブジェクトの構築は、テキスト構文で使用できる引数が位置指定引数または名前付き引数としてトークン化および評価されるまで遅延され、その時点で適切なコンストラクターが呼び出されます。
マークアップ拡張機能の使用は入れ子にすることができます。 最も内側のマークアップ拡張機能が最初に評価されます。 そのため、そのような使用方法を想定し、いずれかの構築パラメーターを、生成される値コンバーター (マークアップ拡張機能など) を必要とする型として宣言できます。
このような処理への依存については、前の例で説明したとおりです。 .NET Framework XAML サービスの XAML オブジェクト ライターは、列挙定数の名前を、ネイティブ レベルでの列挙値に処理します。
また、マークアップ拡張機能の位置指定パラメーターのテキスト構文の処理は、構築引数の型に関連する型コンバーターに依存する場合があります。
引数が位置指定引数と呼ばれるのは、使用方法のトークンに遭遇する順番が割り当てられたコンストラクター パラメーターの順序に対応するためです。 一例として、次のコンストラクター シグネチャを考察します。
public Collate(CollationMode collationMode, object collateThis) {...}
XAML プロセッサは、このマークアップ拡張機能に対し、2 つの位置指定引数を予期しています。 使用方法 {Collate AlphaUp,{x:Reference circularFile}} があった場合、AlphaUp トークンが最初のパラメーターに送られ、CollationMode 列挙体の名前付き定数として評価されます。 内部の x:Reference の結果は 2 つ目のパラメーターに送られ、オブジェクトとして評価されます。
XAML で指定されたマークアップ拡張構文と処理の規則では、引数が位置指定引数の場合でも名前付き引数の場合でも、コンマは引数の区切り記号として使用されます。
位置指定引数の重複するアリティ
XAML オブジェクト ライターが位置指定引数によるマークアップ拡張機能の使用を検出したときに、同じ数の引数 (重複するアリティ) を受け取る複数のコンストラクターの引数が存在している場合でも、必ずしもエラーには該当しません。 実際の動作は、カスタマイズ可能な XAML スキーマ コンテキスト設定である SupportMarkupExtensionsWithDuplicateArity によって異なります。 SupportMarkupExtensionsWithDuplicateArity が true である場合、重複するアリティのみを理由に、XAML オブジェクト ライターから例外がスローされることはありません。 それ以上の動作は、厳密には定義されません。 基本的な設計では、スキーマ コンテキストには固有のパラメーターに対応する型情報が設定されており、明示的なキャストを試みて、重複する候補を照合して最適な一致に該当するシグネチャを確認できるということを前提としています。 XAML オブジェクト ライターで実行されている特定のスキーマ コンテキストによってテストが行われ、そのテストに合格するシグネチャが存在しない場合でも、例外がスローされる可能性があります。
既定では、SupportMarkupExtensionsWithDuplicateArity は、.NET Framework XAML サービスの CLR ベースの XamlSchemaContext で false になります。 したがって、バッキング型のコンストラクターで重複するアリティが存在している状況で、マークアップ拡張機能の使用を検出した場合に、既定の XamlObjectWriter が例外をスローします。
カスタム マークアップ拡張機能の名前付き引数
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 サービスのノード ストリームに保持される理由の 1 つです。多くの場合、読み込みパスのリーダー部分は、必要な XAML スキーマ コンテキストを利用できません。
保存パスで XAML ノード ストリームを操作している場合、通常、オブジェクト グラフ表現には、シリアル化するオブジェクトが最初にマークアップ拡張機能の使用方法と ProvideValue の結果によって提供されたかどうかを判断するための情報は存在しません。 オブジェクト グラフのその他の変化をキャプチャしながら、マークアップ拡張機能の使用方法をラウンドトリップさせる必要があるシナリオでは、元の XAML 入力からマークアップ拡張機能の使用方法の情報を保護するため、独自の対応を講じる必要があります。 たとえば、マークアップ拡張機能の使用方法を復元するには、保存パスでノード ストリームを操作してマークアップ拡張機能の使用方法を復元するか、元の XAML とラウンドトリップされた XAML に対してマージを実行する必要があります。 WPF などの一部の XAML 実装フレームワークでは、中間型 (式) を使用して、マークアップ拡張機能の使用方法が値を指定したケースを表す場合があります。