XAML およびカスタム クラス
更新 : 2007 年 11 月
Extensible Application Markup Language (XAML) では、任意の共通言語ランタイム (CLR) 言語で定義したカスタムのクラスや構造体に、XAML マークアップを使用してアクセスすることができます。Windows Presentation Foundation (WPF) で定義されている XAML タグとカスタム クラスの XAML タグを同じマークアップ ファイル内で使用することもできます。ここでは、カスタム クラスを XAML 要素として使用するために満たされている必要がある要件について説明します。
このトピックには次のセクションが含まれています。
- アプリケーション内のカスタム クラスとアセンブリ内のカスタム クラス
- XAML 要素としてのカスタム クラスの要件
- カスタム クラスのプロパティの XAML 属性としての要件
- カスタム クラスのイベントの XAML イベント ハンドラ属性構文の要件
- コレクション プロパティの作成
- XAML コンテンツ プロパティの宣言
- XAML のシリアル化
- 関連トピック
アプリケーション内のカスタム クラスとアセンブリ内のカスタム クラス
XAML で使用するカスタム クラスを定義するには 2 つの方法があります。1 つは、分離コードや、プライマリ Windows Presentation Foundation (WPF) アプリケーションを生成するその他のコード内で定義する方法です。もう 1 つは、実行可能ファイルや、クラス ライブラリとして使用される DLL などの別のアセンブリ内のクラスとして定義する方法です。これらの方法には、それぞれ利点と欠点があります。
クラス ライブラリを作成する場合の利点は、それらのカスタム クラスを異なる多くのアプリケーションの間で共有できることです。また、独立したライブラリを使用することにより、アプリケーションのバージョン管理や、XAML ページのルート要素として使用するクラスの作成が簡単になります。
カスタム クラスをアプリケーション内で定義する場合の利点は、比較的軽量であることです。メインの実行可能ファイルとは別のアセンブリを導入する場合に生じる配置やテストの問題を最小限に抑えることができます。ただし、XAML ページのルート要素と同じアセンブリ内で定義されるクラスを使用できないという、1 つの大きな欠点があります。
同じアセンブリで定義するか別のアセンブリで定義するかに関係なく、カスタム クラスを XAML で要素として使用するためには、CLR 名前空間と XML 名前空間にマッピングする必要があります。「XAML 名前空間および名前空間の割り当て」を参照してください。
XAML 要素としてのカスタム クラスの要件
カスタム クラスをオブジェクト要素としてインスタンス化できるようにするには、次の要件が満たされている必要があります。
カスタム クラスがパブリックであり、既定の (パラメータなしの) パブリック コンストラクタがサポートされている (マネージ コードの構造体ではこのようなコンストラクタが暗黙的にサポートされます)。
カスタム クラスが入れ子になっていない (入れ子になったクラスやその構文の "ドット" は、添付プロパティなどの他の WPF 機能に干渉します)。
オブジェクト要素構文を使用できるようになると、そのオブジェクトを値型とする他のパブリック プロパティでプロパティ要素構文を使用できるようにもなります。これは、オブジェクト要素としてインスタンス化できるようになったオブジェクトは、そのプロパティのプロパティ要素値として設定できるためです。
構造体
カスタム型として定義する構造体は常に WPF 内の XAML に構築できます。これは、CLR コンパイラにより、すべてのプロパティ値を既定値に初期化する構造体の既定のコンストラクタが明示的に作成されるためです。既定のコンストラクタの動作やある構造体におけるオブジェクト要素の使用が望ましくない場合もあります。その理由は、構造体は値と関数を概念的に一体として入れることを意図しており、このため含まれた値が排他的な解釈を持つ可能性があり、その結果プロパティがまったく設定できない場合が生じるためです。このような構造体の WPF の例は GridLength です。一般的にこのような構造体は、異なる解釈や構造体の値のモードを作成する文字列の規則を使って値を属性形式で表示できるようにする型コンバータを実装する必要があります。さらに、この構造体は、コード構築が既定以外のコンストラクタによって行われる場合にも、同じ動作をする必要があります。
カスタム クラスのプロパティの XAML 属性としての要件
プロパティでは、値型 (プリミティブなど) を参照するか、既定のコンストラクタまたはクラス レベルの専用の型コンバータを持つ型のクラスを使用する必要があります。
そのほか、抽象クラス型やインターフェイスを参照することもできます。抽象クラスやインターフェイスの場合は、そのインターフェイスを実装する実用クラスのインスタンスや、その抽象クラスを派生するクラスのインスタンスによって、実行時にプロパティ値が設定されるものと想定されます。
プロパティは抽象クラスで宣言できますが、その抽象クラスから派生する実用クラスでしか設定できません。これは、クラスのオブジェクト要素を作成するには、クラスにパブリックな既定のコンストラクタが必要であるためです。
型コンバータによって実現される属性構文
属性として割り当てられた専用の型コンバータをクラス レベルで用意すると、適用される型変換によって、その型をインスタンス化する必要がある任意のプロパティで属性構文を使用できるようになります。型コンバータを用意しても、その型のオブジェクト要素の使用は有効になりません。オブジェクト要素の使用は、その型の既定のコンストラクタが存在する場合にのみ有効です。したがって、型コンバータによって実現されるプロパティは、その型自体でオブジェクト要素構文もサポートされていない限り、一般にプロパティ構文では使用できません。例外として、プロパティ要素に文字列を含める場合には、プロパティ要素構文を指定できます。この使用方法は、実際には属性構文の使用方法と本質的に変わらず、属性値で空白の堅牢な処理が必要な場合を除いては一般的ではありません。文字列を受け取るプロパティ要素の使用方法と、それと同等な属性の使用方法を次の例に示します。
<Button>Hallo!
<Button.Language>
de-DE
</Button.Language>
</Button>
<Button Language="de-DE">Hallo!</Button>
XAML で属性構文は使用できるが、オブジェクト要素を含むプロパティ要素構文は使用できないプロパティの一例となるのが、Cursor 型を受け取るさまざまなプロパティです。Cursor クラスには専用の型コンバータ CursorConverter がありますが、既定のコンストラクタは公開されていません。このため、Cursor プロパティは、実際の Cursor 型は参照型であるにもかかわらず、属性構文でしか設定できません。
プロパティごとの型コンバータ
プロパティ レベルで型コンバータを宣言することもできます。これにより、プロパティの型のオブジェクトをインラインでインスタンス化する "ミニ言語" が実現されます。これは、渡される属性の文字列値を ConvertFrom 操作の入力として適切な型に基づいて処理することによって行われます。通常、これは便利なアクセサを提供するために行われるものであり、XAML でプロパティを設定できるようにするための唯一の手段として使用されるものではありません。ただし、使用する既存の CLR 型に既定のコンストラクタも、属性として割り当てられた型コンバータも用意されていない場合に、属性の型コンバータを使用することもできます。たとえば、WPF API に含まれている、CultureInfo 型を受け取るプロパティはその一例です。この場合、WPF では、フレームワークの以前のバージョンで使用されていた互換性と移行のシナリオを考慮して既存の Microsoft .NET Framework CultureInfo 型を使用していますが、CultureInfo 型では、直接 XAML プロパティの値として使用するために必要なコンストラクタや型レベルの型変換はサポートされていません。
XAML を使用するプロパティを公開するときには常に (コントロールを作成する場合は特に)、そのプロパティを依存関係プロパティで補足することを強くお勧めします。DependencyProperty サポートを使用することでパフォーマンスを向上できるため、XAML プロセッサの既存の Windows Presentation Foundation (WPF) 実装を使用する場合は、これが特に当てはまります。依存関係プロパティは、XAML でアクセス可能なプロパティに対して期待されるプロパティ システム機能をそのプロパティのために公開します。これには、アニメーション、データ バインディング、スタイルのサポートなどの機能が含まれます。詳細については、「カスタム依存関係プロパティ」および「XAML 読み込みと依存関係プロパティ」を参照してください。
型コンバータの作成と割り当て
プロパティ型の型変換を提供するためにカスタム TypeConverter 派生クラスの作成が必要になることはよくあります。XAML の使用をサポートする型コンバータの派生および作成の手順や、TypeConverterAttribute の適用方法については、「TypeConverters および XAML」を参照してください。
カスタム クラスのイベントの XAML イベント ハンドラ属性構文の要件
イベントを CLR イベントとして使用できるようにするには、既定のコンストラクタをサポートするクラスか、派生クラスでイベントにアクセスできる抽象クラスで、パブリック イベントとして公開する必要があります。CLR イベントをルーティング イベントとして便利に使用できるようにするには、明示的な add メソッドと remove メソッドを実装する必要があります。それらのメソッドは、CLR イベント シグネチャのハンドラを追加および削除し、それらのハンドラを AddHandler メソッドと RemoveHandler メソッドに転送します。これらのメソッドは、イベントがアタッチされているインスタンスのルーティング イベント ハンドラ ストアのハンドラを追加または削除します。
メモ : |
---|
AddHandler を使用して直接ルーティング イベントのハンドラを登録し、ルーティング イベントを公開する CLR イベントを意図的に定義しないようにすることもできます。この方法を使用すると、そのイベントで XAML 属性構文を使用してハンドラをアタッチできなくなるほか、多くの場合、結果となるクラスによって提供されるクラス オブジェクト モデルの XAML ビューの透過性も低下するため、一般に推奨されません。 |
コレクション プロパティの作成
コレクション型を受け取るプロパティには、コレクションに追加されるオブジェクトを指定できる XAML 構文があります。この構文には注目すべき機能が 2 つあります。
コレクション オブジェクトとなるオブジェクトをオブジェクト要素構文で指定する必要はありません。コレクション型を受け取るプロパティを XAML で指定するたびに、そのコレクション型の存在が暗黙的に示されます。
コレクション プロパティの子要素が処理されると、コレクションのメンバになります。通常は、コレクションのメンバにコードでアクセスするには、Add などのコレクション メソッドを使用するか、コレクションのインデクサ プロパティを使用します。しかし、XAML 構文ではメソッドやインデクサはサポートされていません。コレクションは要素ツリーの構築のためにごく一般的に必要になるため、それらのコレクションを宣言型の XAML で設定するための何らかの方法が必要です。このため、コレクション プロパティの子要素は、コレクション プロパティ型の値であるコレクションに追加することによって処理されます。
WPF XAML プロセッサは、コレクション プロパティを構成するものとして次の定義を使用します。プロパティのプロパティ型で次のいずれかを実装する必要があります。
IList を実装します。
IDictionary、またはこれと同等のジェネリック クラス (IDictionary<TKey, TValue>) を実装します。
Array から派生します (XAML の配列の詳細については、「x:Array のマークアップ拡張機能」を参照してください)。
IAddChild (WPF で定義されているインターフェイス) を実装します。
これらの型には、それぞれ Add メソッドが含まれます。Add メソッドは、XAML プロセッサによって、基になるコレクションに項目を追加するために使用されます。
メモ : |
---|
ジェネリック List インターフェイスおよびジェネリック Dictionary インターフェイス (IList<T> および IDictionary<TKey, TValue>) は、WPF XAML プロセッサによるコレクション検出ではサポートされていません。一方、List<T> クラスおよび Dictionary<TKey, TValue> クラスを基本クラスとして使用することはできます。それぞれ、IList と IDictionary を直接実装しているためです。 |
コレクションを受け取るプロパティを宣言するときには、その型の新しいインスタンスでそのプロパティ値がどのように初期化されるかに注意する必要があります。プロパティを依存関係プロパティとして実装しない場合は、コレクション型のコンストラクタを呼び出す補足フィールドを使用できます。プロパティが依存関係プロパティの場合は、そのコレクション プロパティを既定の型コンストラクタの一部として初期化する必要がある可能性があります。これは、依存関係プロパティはメタデータから既定値を受け取りますが、コレクション プロパティの初期値は、通常、共有された静的コレクションにはしないためです (含まれている型インスタンスごとにコレクション インスタンスが必要です)。詳細については、「カスタム依存関係プロパティ」を参照してください。
コレクション プロパティにカスタム コレクション型を実装することもできます。暗黙的なコレクション プロパティとして扱われるため、カスタム コレクション型では既定のコンストラクタを用意する必要はありません。既定のコンストラクタがなくても、XAML で暗黙的に使用できます。ただし、コレクション型の既定のコンストラクタを用意することもできます。既定のコンストラクタを用意しないと、そのコレクションをオブジェクト要素として明示的に宣言できないため、既定のコンストラクタを用意する価値がある場合もあります。マークアップ スタイルの問題から明示的なコレクションが好まれることもあります。また、既定のコンストラクタを用意すると、そのコレクション型をプロパティ値として使用する新しいオブジェクトを作成するときの初期化要件が単純化されます。
XAML コンテンツ プロパティの宣言
XAML 言語では、XAML コンテンツ プロパティの概念が定義されています。オブジェクト構文で使用できる各クラスは、XAML コンテンツ プロパティを 1 つだけ持つことができます。プロパティをクラスの XAML コンテンツ プロパティとして宣言するには、クラス定義の一部として ContentPropertyAttribute を適用し、目的の XAML コンテンツ プロパティの名前をその属性の Name として指定します。
コレクション プロパティを XAML コンテンツ プロパティとして指定することもできます。この結果、そのプロパティの使用方法で、コレクション オブジェクト要素やプロパティ要素のタグを間にはさまずに、オブジェクト要素に 1 つ以上の子要素を持たせることができます。それらの要素は XAML コンテンツ プロパティの値として扱われ、対応するコレクション インスタンスに追加されます。
既存の WPF XAML コンテンツ プロパティの中には、プロパティ型として Object を使用するものがあります。この場合、XAML コンテンツ プロパティで、String などのプリミティブ値も、1 つの参照オブジェクト値も受け取ることができます。このモデルに従う場合は、型の判定や使用できる型の処理をその型で行う必要があります。Object 型モデルは一般に、オブジェクト コンテンツを文字列として追加する単純な手段と (既定のプレゼンテーションが適用されます)、既定以外のプレゼンテーションを指定するオブジェクト コンテンツを追加する高度な手段の両方をサポートする場合に使用されます。
XAML のシリアル化
コントロールを作成する場合など、一部のシナリオでは、XAML でインスタンス化できるオブジェクト表現を同等の XAML に再度シリアル化できるようにする必要がある場合もあります。シリアル化の要件については、ここでは説明しません。「コントロールの作成の概要」および「要素のツリーおよびシリアル化」を参照してください。