添付プロパティの概要
添付プロパティは、XAML によって定義された概念です。 添付プロパティは、任意の依存関係オブジェクトに設定可能なグローバル プロパティの型として使用されることを意図しています。 Windows Presentation Foundation (WPF) では通常、添付プロパティは従来のプロパティ "ラッパー" を含まない依存関係プロパティの特殊な形式として定義されています。
前提条件
この記事では、ユーザーが Windows Presentation Foundation (WPF) クラスの既存の依存関係プロパティの使用という観点から依存関係プロパティを理解し、「依存関係プロパティの概要」を読んでいることを前提としています。 この記事の例について理解するには、XAML および WPF アプリケーションの記述方法について知っておく必要もあります。
添付プロパティを使用する理由
添付プロパティの目的の 1 つは、親要素に定義されているプロパティに対する一意の値を、異なる子要素が指定できるようにすることです。 このシナリオの適用例として、子要素から親要素に、ユーザー インターフェイス (UI) での表示方法を通知させることがあります。 1 つの例として、DockPanel.Dock プロパティがあります。 DockPanel.Dock プロパティは添付プロパティとして作成されます。このプロパティが、DockPanel 自体ではなく、DockPanel の内部に含まれる要素に設定されるように設計されているためです。 DockPanel クラスでは DockProperty という名前の静的な DependencyProperty フィールドが定義されており、GetDock および SetDock メソッドが添付プロパティのパブリック アクセサーとして提供されます。
XAML の添付プロパティ
XAML では、構文 AttachedPropertyProvider.PropertyName を使用して添付プロパティを設定します
XAML での DockPanel.Dock の設定方法の例を次に示します。
<DockPanel>
<TextBox DockPanel.Dock="Top">Enter text</TextBox>
</DockPanel>
使い方は静的プロパティに少し似ています。名前で指定されたインスタンスを参照するのではなく、添付プロパティを所有して登録する型 DockPanel を常に参照します。
さらに、XAML の添付プロパティはマークアップに設定する属性であるため、設定操作にのみ関連性があります。 XAML でプロパティを直接取得することはできませんが、スタイルのトリガー (詳細については、「スタイルとテンプレート」を参照) などの値を比較するための間接的な機構があります。
WPF での添付プロパティの実装
Windows Presentation Foundation (WPF) では、WPF 型の UI 関連の添付プロパティのほとんどが、依存関係プロパティとして実装されます。 添付プロパティは XAML の概念であり、依存関係プロパティは WPF の概念です。 WPF の添付プロパティは依存関係プロパティであるため、プロパティのメタデータ、そしてその既定値といった依存関係プロパティの概念がサポートされます。
所有する型による添付プロパティの使用方法
添付プロパティはどのオブジェクトにも設定できますが、プロパティを設定したことによって自動的に意味のある結果が得られるわけでも、値が別のオブジェクトによって使用されるわけでもありません。 一般に、添付プロパティの目的は、想定されるさまざまなクラス階層または論理関係から生じるオブジェクトが、添付プロパティを定義する型に共通する情報をレポートできるようにすることです。 添付プロパティを定義する型は、一般的に次のいずれかのモデルに従っています。
添付プロパティを定義する型が、添付プロパティの値を設定する要素の親要素になるように設計されている。 この型の子オブジェクトは、一部のオブジェクト ツリー構造で内部ロジックを反復し、値を取得して、その値に対する処理を実行します。
添付プロパティを定義する型が、想定されるさまざまな親要素およびコンテンツ モデルの子要素として使用される。
添付プロパティを定義する型が、サービスを表す。 その他の型は、添付プロパティの値を設定します。 プロパティを設定する要素がサービスのコンテキストで評価されると、添付プロパティの値がサービス クラスの内部ロジックにより取得されます。
親定義の添付プロパティの例
WPF で添付プロパティが定義される最も一般的なシナリオは、親要素で子要素のコレクションがサポートされ、さらに動作の詳細が子要素ごとにレポートされるような動作が実装される場合です。
DockPanel では DockPanel.Dock 添付プロパティが定義されており、DockPanel には、そのレンダリング ロジック (具体的には、MeasureOverride および ArrangeOverride) の一部としてクラスレベルのコードが含まれます。 DockPanel インスタンスでは、直下の子要素のいずれかに DockPanel.Dock の値が設定されているかどうかが常に確認されます。 設定されている場合は、その値が、その子要素に適用されるレンダリング ロジックの入力になります。 入れ子になった DockPanel インスタンスではそれぞれが所有する直下の子要素のコレクションが処理されますが、DockPanel による DockPanel.Dock の値の処理方法は、実装によって異なります。 直接の親以外の要素に影響を与える添付プロパティを所有することは、理論上は可能です。 DockPanel.Dock 添付プロパティが要素で設定されており、その要素に影響を与える DockPanel 親要素がない場合、エラーまたは例外は発生しません。 これは単に、グローバル プロパティ値が設定されているものの、その情報を消費する最新の DockPanel の親が存在しないことを意味します。
コードの添付プロパティ
WPF の添付プロパティには、取得と設定のアクセスを簡単にする、一般的な 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);
Dim myDockPanel As New DockPanel()
Dim myCheckBox As New CheckBox()
myCheckBox.Content = "Hello"
myDockPanel.Children.Add(myCheckBox)
DockPanel.SetDock(myCheckBox, Dock.Top)
XAML と同様、コードの 4 行目に myDockPanel
の子要素として myCheckBox
が追加されていない場合、45行目で例外が発生することはありませんが、プロパティ値が DockPanel の親と対話しないため、処理が行われません。 DockPanel 親要素の存在と組み合わされた子要素で値 DockPanel.Dock が設定されている場合にのみ、レンダリングされたアプリケーションで有効な動作が発生します。 (この場合、添付プロパティを設定してからツリーに接続するか、ツリーに接続してから添付プロパティを設定することができます。どちらのアクションでも、結果は同じです。)
添付プロパティのメタデータ
プロパティを登録すると、そのプロパティがレンダリング、測定などに影響するかどうかといったプロパティの特性を指定するように FrameworkPropertyMetadata が設定されます。 添付プロパティのメタデータは、一般的に依存関係プロパティとの違いがありません。 オーバーライドの既定値を添付プロパティのメタデータに指定すると、その値がオーバーライドするクラスのインスタンスの暗黙的な添付プロパティの既定値になります。 具体的には、一部のプロセスが添付プロパティの Get
メソッド アクセサーを使用してそのプロパティの値のクエリを行った場合に、メタデータを指定したクラスのインスタンスが指定されており、その添付プロパティの値が設定されていないと、既定値がレポートされます。
プロパティでプロパティ値の継承を有効にする場合は、未接続の依存関係プロパティではなく添付プロパティを使用する必要があります。 詳細については、「プロパティ値の継承」を参照してください。
カスタム添付プロパティ
添付プロパティを作成するタイミング
添付プロパティは、定義クラスではないクラスで使用できるプロパティ設定機構を用意する理由がある場合に作成できます。 この最も一般的なシナリオが、レイアウトです。 既存のレイアウト プロパティの例は、DockPanel.Dock、Panel.ZIndex、Canvas.Top などです。 これによって実現するシナリオは、レイアウト制御要素の子要素として存在する要素が、レイアウト親要素に対して個別にレイアウト要件を表現するというものです。親が添付プロパティとして定義したプロパティ値を、個々の子要素が設定します。
クラスがサービスを表しており、クラスでサービスをより透過的に統合できるようにしたい場合にも、添付プロパティを使用します。
さらに別のシナリオは、 [プロパティ] ウィンドウの編集など、Visual Studio WPF デザイナーのサポートを利用する場合です。 詳しくは、「コントロールの作成の概要」を参照してください。
前述のように、プロパティ値の継承を使用する場合には、添付プロパティを登録する必要があります。
添付プロパティの作成方法
クラスにおいて、他の型で使用する目的のみで添付プロパティが定義されている場合は、そのクラスを DependencyObject から派生させる必要はありません。 ただし、添付プロパティが依存関係プロパティでもある、WPF モデル全体を使用する場合は、DependencyObject から派生させる必要があります。
DependencyProperty 型の public static readonly
フィールドを宣言することにより、添付プロパティを依存関係プロパティとして定義します。 このフィールドは、RegisterAttached メソッドの戻り値を使用して定義します。 識別フィールドとそれが表すプロパティの名前付けに関して確立されている WPF のパターンに従うには、Property
の文字列が付加され、フィールド名が添付プロパティ名と一致している必要があります。 添付プロパティのプロバイダーでは、添付プロパティのアクセサーとして GetPropertyName および SetPropertyName の静的メソッドを指定する必要があります。これを行わないと、プロパティ システムで添付プロパティを使用できません。
注意
添付プロパティの get アクセサーを省略すると、プロパティのデータ バインディングが Visual Studio や Blend for Visual Studio などのデザイン ツールで動作しません。
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);
}
Public Shared ReadOnly IsBubbleSourceProperty As DependencyProperty = DependencyProperty.RegisterAttached("IsBubbleSource", GetType(Boolean), GetType(AquariumObject), New FrameworkPropertyMetadata(False, FrameworkPropertyMetadataOptions.AffectsRender))
Public Shared Sub SetIsBubbleSource(ByVal element As UIElement, ByVal value As Boolean)
element.SetValue(IsBubbleSourceProperty, value)
End Sub
Public Shared Function GetIsBubbleSource(ByVal element As UIElement) As Boolean
Return CType(element.GetValue(IsBubbleSourceProperty), Boolean)
End Function
添付プロパティの属性
WPF では複数の .NET 属性が定義されています。これは添付プロパティに関する情報をリフレクション プロセス、リフレクションおよびプロパティ情報の一般的なユーザー (デザイナーなど) に提供することを目的としています。 添付プロパティに含まれる型は膨大な範囲に及ぶため、デザイナーには XAML を使用する特定のテクノロジの実装に定義されたすべての添付プロパティのグローバル リストがユーザーに表示されないようにするための手段が必要となります。 WPF で添付プロパティに対して定義されている .NET 属性を使用すると、指定した添付プロパティのみをプロパティ ウィンドウに表示することができます。 また、この属性をカスタム添付プロパティに適用するという選択肢もあります。 .NET 属性の目的および構文は、次の参照ページに記載されています。
添付プロパティの詳細情報
添付プロパティの作成の詳細については、「方法: 添付プロパティを登録する」を参照してください。
依存関係プロパティおよび添付プロパティの高度な使用シナリオについては、「カスタム依存関係プロパティ」を参照してください。
プロパティは添付プロパティとしても依存関係プロパティとしても登録できますが、"ラッパー" 実装は公開したままにすることができます。 この場合、プロパティをその要素に設定することも、XAML の添付プロパティの構文を使用して任意の要素に設定することもできます。 標準使用および添付による使用の両方に適したシナリオでのプロパティの例は、FrameworkElement.FlowDirection です。
関連項目
.NET Desktop feedback