添付プロパティの概要
更新 : 2007 年 11 月
添付プロパティとは、Extensible Application Markup Language (XAML) によって定義される概念です。添付プロパティの目的は、任意のオブジェクトに対して設定可能な一種のグローバル プロパティとして使用することです。Windows Presentation Foundation (WPF) では一般に、添付プロパティは、従来のプロパティ "ラッパー" を持たない特殊な形式の依存関係プロパティとして定義されます。
このトピックには次のセクションが含まれています。
- 必要条件
- 添付プロパティを使用する理由
- XAML での添付プロパティ
- 所有する型が添付プロパティを使用する方法
- コードでの添付プロパティ
- 添付プロパティのメタデータ
- カスタム添付プロパティ
- 添付プロパティの詳細情報
- 関連トピック
必要条件
このトピックでは、ユーザーが Windows Presentation Foundation (WPF) クラスの既存の依存関係プロパティの使用という観点から依存関係プロパティを理解し、「依存関係プロパティの概要」トピックを通読していることを前提としています。このトピックの例を実行するには、Extensible Application Markup Language (XAML) について理解し、WPF アプリケーションの記述に精通している必要があります。
添付プロパティを使用する理由
添付プロパティの目的の 1 つは、実際には親要素で定義されるプロパティについて、子要素がそれぞれ別の値を指定できるようにすることです。このシナリオは、たとえば、子要素をユーザー インターフェイス (UI) にどのように表示するかを子要素から親要素に通知させるという状況に応用されます。DockPanel.Dock プロパティがその一例です。DockPanel.Dock プロパティが添付プロパティとして作成されるのは、このプロパティが DockPanel 自体ではなく DockPanel に含まれる要素に対して設定するように設計されているためです。DockPanel クラスでは、DockProperty という名前の静的 DependencyProperty フィールドが定義されており、添付プロパティのパブリック アクセサとして GetDock メソッドと SetDock メソッドが定義されています。
XAML での添付プロパティ
XAML では、AttachedPropertyProvider.PropertyName 構文を使用して添付プロパティを設定します。
XAML で DockPanel.Dock を設定する方法を次の例に示します。
<DockPanel>
<CheckBox DockPanel.Dock="Top">Hello</CheckBox>
</DockPanel>
使用方法が静的プロパティに少し似ていることに注意してください。名前で指定されたインスタンスを参照するのではなく、添付プロパティを所有および登録する DockPanel 型を常に参照します。
また、XAML では、添付プロパティはマークアップ内で設定する属性であるので、有効な操作は設定のみです。XAML の中でプロパティを直接取得することはできませんが、スタイルのトリガなど、間接的に値を比較する機構があります (詳細については、「スタイルとテンプレート」を参照してください)。
WPF での添付プロパティの実装
Windows Presentation Foundation (WPF) において、WPF の型に対して存在する添付プロパティのほとんどは、依存関係プロパティとして実装されます。添付プロパティは XAML の概念であり、依存関係プロパティは WPF の概念です。WPF の添付プロパティは依存関係プロパティであるので、プロパティ メタデータなどの依存関係プロパティの概念や、プロパティ メタデータからの既定値がサポートされます。
所有する型が添付プロパティを使用する方法
添付プロパティはどのオブジェクトに対しても設定可能ですが、プロパティを設定することで具体的な結果が得られたり、その値が他のオブジェクトから使用されたりするわけではありません。大まかにいって、添付プロパティの目的は、さまざまなクラス階層または論理上のリレーションシップから発生するオブジェクトのそれぞれが、所有する型に対して共通の情報を報告できるようにすることです。添付プロパティを定義する型は一般に、次のいずれかのモデルに従います。
添付プロパティを定義する型は、添付プロパティの値を設定する要素の親要素になることができるように設計されています。この型は、内部ロジックを通じて自身の子要素を反復処理して値を取得し、その値に対するなんらかの処理を行います。
添付プロパティを定義する型は、さまざまな親要素およびコンテンツ モデルの子要素として使用されます。
添付プロパティを定義する型は、サービスを表します。その他の型は、添付プロパティの値を設定します。プロパティを設定する要素がサービスのコンテキスト内で評価されるときに、サービス クラスの内部ロジックを通じて添付プロパティの値が取得されます。
親要素で定義された添付プロパティの例
WPF が添付プロパティを定義する最も一般的なシナリオは、親要素が子要素のコレクションをサポートすると同時に、親要素が実装した動作の詳細を子要素ごとに別個に報告させるというものです。
DockPanel では DockPanel.Dock 添付プロパティが定義され、DockPanel のレンダリング ロジックの一部としてクラス レベルのコードが設定されています (具体的には MeasureOverride と ArrangeOverride)。DockPanel のインスタンスは、直接の子要素が DockPanel.Dock の値を設定しているかどうかを常に確認します。設定されている場合は、その値を入力とするレンダリング ロジックを、その子要素に適用します。入れ子になった DockPanel インスタンスは、それぞれ自身の直接の子要素のコレクションを扱いますが、その動作は実装によって異なります。直接の親以外の要素に作用する添付プロパティも、理論的には可能です。DockPanel.Dock 添付プロパティが設定されている要素が、作用対象の DockPanel 親要素を持たない場合も、エラーや例外は発生しません。これは、グローバル プロパティ値が設定されたけれども、その情報を利用できる親 DockPanel が現在存在しないことを意味します。
コードでの添付プロパティ
WPF の添付プロパティには、get および set のアクセスを容易にするための標準的な CLR "ラッパー" メソッドはありません。これは、添付プロパティが設定されたインスタンスの CLR 名前空間に、その添付プロパティが属しているとは限らないからです。ただし、XAML リーダーは、XAML の処理時にこれらの値を設定できる必要があります。添付プロパティを有効にするには、添付プロパティの所有者型が GetPropertyName および SetPropertyName の形式で専用アクセサ メソッドを実装する必要があります。コードで添付プロパティを取得または設定するときも、この専用アクセサ メソッドを使用する必要があります。コードの観点から言うと、添付プロパティは、プロパティ アクセサではなくメソッド アクセサを持つバッキング フィールドに似ています。そしてそのバッキング フィールドは、任意のオブジェクトに存在し得るもので、具体的な定義を必要とするものではありません。
コードで添付プロパティを設定する方法を次の例に示します。この例では、myCheckBox は CheckBox クラスのインスタンスです。
DockPanel myDockPanel = new DockPanel();
CheckBox myCheckBox = new CheckBox();
myCheckBox.Content = "Hello";
myDockPanel.Children.Add(myCheckBox);
DockPanel.SetDock(myCheckBox, Dock.Top);
XAML の場合と同様に、コードの 3 行目の時点で myCheckBox が myDockPanel の子要素として追加されていなくても、コードの 4 行目で例外が発生することはありませんが、プロパティ値と親 DockPanel とが相互作用しないので、何も実行されません。子要素で DockPanel.Dock 値が設定されていて、親要素の DockPanel が存在する場合にのみ、レンダリングされたアプリケーション内で有効な動作が実行されます。
添付プロパティのメタデータ
プロパティを登録するときに、FrameworkPropertyMetadata を設定してプロパティの特性を指定します。たとえば、プロパティがレンダリングやサイズ測定に影響を及ぼすかどうかを指定します。通常、添付プロパティのメタデータは、依存プロパティのメタデータと違いはありません。オーバーライドで添付プロパティ メタデータに既定値を指定した場合、その値は、オーバーライドしたクラスのインスタンスの暗黙の添付プロパティの既定値となります。具体的には、なんらかのプロセスが、Get メソッド アクセサを通じて添付プロパティの値をクエリするときに、メタデータを設定したクラスのインスタンスを指定した場合、その添付プロパティの値が他では特に設定されていないときには、既定値が通知されます。
プロパティ値の継承を有効にするときには、添付プロパティ以外の依存関係プロパティではなく、添付プロパティを使用する必要があります。詳細については、「プロパティ値の継承」を参照してください。
カスタム添付プロパティ
いつ添付プロパティを作成するか
添付プロパティを作成するのは、プロパティを定義するクラス以外のクラスでプロパティを設定するための機構が必要な場合です。この最も一般的なシナリオはレイアウトです。既存のレイアウト プロパティの例としては、DockPanel.Dock、Panel.ZIndex、Canvas.Top などがあります。これによって実現するシナリオは、レイアウト制御要素の子要素として存在する要素が、レイアウト親要素に対して個別にレイアウト要件を表現するというものです。親が添付プロパティとして定義したプロパティ値を、個々の子要素が設定します。
添付プロパティを使用するシナリオには、この他に、クラスによってサービスを表す場合に、クラスとサービスとをより透過的に統合できるようにするというものがあります。
さらに別のシナリオとしては、Visual Studio 2008 の WPF デザイナのサポート ([プロパティ] ウィンドウでの編集など) に対応するというものもあります。詳細については、「コントロールの作成の概要」を参照してください。
既に述べたように、プロパティ値の継承を使用する場合には、添付プロパティとして登録する必要があります。
添付プロパティの作成方法
クラスで定義する添付プロパティが、他の型での使用のみを目的としている場合は、このクラスが DependencyObject から派生していなくてもかまいません。しかし、添付プロパティが依存関係プロパティでもあるという全体的な WPF モデルに従う場合は、DependencyObject から派生している必要があります。
添付プロパティを依存関係プロパティとして定義するには、DependencyProperty 型の public static readonly フィールドを宣言します。このフィールドを定義するには、RegisterAttached メソッドの戻り値を使用します。フィールド名は、添付プロパティと同じ名前に文字列 Property を付加したものである必要があります。識別するフィールドとそのフィールドが表すプロパティに関する、WPF で確立された命名パターンに従うためです。また、添付プロパティのプロバイダは、添付プロパティのアクセサとして GetPropertyName および SetPropertyName の静的メソッドを持つ必要があります。これらのメソッドがないプロパティ システムは、添付プロパティを使用することができません。
Get アクセサ
GetPropertyName アクセサのシグネチャの形式は次のとおりです。
public static object GetPropertyName(object target)
target オブジェクトは、実装内で具体的な型として指定できます。たとえば、DockPanel.GetDock メソッドでは、このパラメータの型が UIElement となっています。これは、添付プロパティが UIElement インスタンスに対してのみ設定されることになっているからです。
戻り値は、実装内で具体的な型として指定できます。たとえば、GetDock メソッドの戻り値の型は Dock ですが、この値はその列挙体にのみ設定可能だからです。
Set アクセサ
SetPropertyName アクセサのシグネチャの形式は次のとおりです。
public static void SetPropertyName(object target, object value)
target オブジェクトは、実装の中で具体的な型として指定できます。たとえば、SetDock メソッドのこのオブジェクトの型は UIElement となっています。これは、添付プロパティが UIElement インスタンスに対してのみ設定されることになっているからです。
value オブジェクトは、実装の中で具体的な型として指定できます。たとえば、SetDock メソッドのこのパラメータの型は Dock となっています。この値は、その列挙体にのみ設定可能だからです。このメソッドの値は、マークアップ内での添付プロパティの使用が XAML ローダーによって検出されたときのローダーからの入力値であることに注意してください。この入力は、マークアップ内では XAML の属性値として指定される値です。したがって、属性値 (最終的には文字列) から適切な型を作成するには、使用する型でサポートされる型変換、値のシリアライザ、またはマークアップ拡張機能が必要です。
依存関係プロパティの登録 (RegisterAttached メソッドを使用) と、GetPropertyName アクセサおよび SetPropertyName アクセサの例を次に示します。この例では、添付プロパティ名は IsBubbleSource です。したがって、アクセサの名前は GetIsBubbleSource および SetIsBubbleSource となります。
public static readonly DependencyProperty IsBubbleSourceProperty = DependencyProperty.RegisterAttached(
"IsBubbleSource",
typeof(Boolean),
typeof(AquariumObject),
new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.AffectsRender)
);
public static void SetIsBubbleSource(UIElement element, Boolean value)
{
element.SetValue(IsBubbleSourceProperty, value);
}
public static Boolean GetIsBubbleSource(UIElement element)
{
return (Boolean)element.GetValue(IsBubbleSourceProperty);
}
添付プロパティの属性
WPF には、.NET Framework 属性 がいくつか定義されています。これらは、リフレクション プロセス、およびリフレクションとプロパティ情報の一般的なユーザー (デザイナなど) に対して、添付プロパティについての情報を提供することを目的としています。添付プロパティはスコープが限定されていない型であるため、XAML を使用する特定の技術の実装で定義されているすべての添付プロパティのグローバル リストでユーザーを圧倒させないようにするための方法が必要です。WPF が添付プロパティに対して定義する .NET Framework 属性 を使用することで、特定の添付プロパティをプロパティ ウィンドウに表示するかどうかという状況に対応できます。これらの属性は、独自のカスタム添付プロパティに適用することもできます。.NET Framework 属性 の目的と構文については、以下の該当するリファレンス ページを参照してください。
添付プロパティの詳細情報
添付プロパティの作成の詳細については、「方法 : 添付プロパティを登録する」を参照してください。
依存関係プロパティと添付プロパティの使用方法のより高度なシナリオについては、「カスタム依存関係プロパティ」を参照してください。
プロパティを添付プロパティおよび依存関係プロパティとして登録するけれども "ラッパー" 実装も公開するということも可能です。この場合、プロパティはその要素に対して設定することも、XAML 添付プロパティ構文をとおして任意の要素に対して設定することもできます。標準の使用法と添付での使用法の両方について適切なシナリオを持つプロパティの例が、FrameworkElement.FlowDirection です。