1. ホーム
  2. .net

[解決済み] WPFのdependencyプロパティとattachedプロパティの違いは何ですか?

2023-02-15 05:31:16

質問

WPFの(カスタム)依存性プロパティとattachedプロパティの違いは何ですか? それぞれどのような用途がありますか? 一般的に、実装はどのように違うのでしょうか?

どのように解決するのですか?

概要

この件に関するドキュメントがほとんどなかったため、いくつかの方法で ソース コード を突き合わせる必要がありましたが、ここに答えがあります。

依存性プロパティを通常のプロパティとして登録することと、アタッチドプロパティとして登録することの違いは、"哲学的なもの以外に、( 通常のプロパティは宣言する型とその派生型によって使用されることを意図しており、アタッチドプロパティは任意の DependencyObject インスタンス というのも、@ReedCopsey さんの回答に対するコメントで @MarqueIV さんが気づいたように、通常のプロパティでも任意の DependencyObject インスタンスで使用することができます。

さらに、attachedプロパティがquot;type of dependency property"であると述べている他の回答には同意しかねます。フレームワークは、プロパティがアタッチされたものとして登録されたかどうかを気にしませんし、判断することさえできません(この情報は記録されないという意味で、無関係だからです)。実際には、すべてのプロパティは、それらが添付されたプロパティであるかのように登録されますが、通常のものの場合には、それらの動作をわずかに変更するいくつかの追加的なことが行われます。

コードの抜粋

自分でソースコードを調べる手間を省くために、ここでは何が起こるかを要約して説明します。

メタデータを指定せずにプロパティを登録する場合、以下のように呼び出します。

DependencyProperty.Register(
    name: "MyProperty",
    propertyType: typeof(object),
    ownerType: typeof(MyClass))

収量 全く同じ を呼び出すのと同じ結果になります。

DependencyProperty.RegisterAttached(
    name: "MyProperty",
    propertyType: typeof(object),
    ownerType: typeof(MyClass))

ただし、メタデータを指定する際に

DependencyProperty.Register(
    name: "MyProperty",
    propertyType: typeof(object),
    ownerType: typeof(MyClass),
    typeMetadata: new FrameworkPropertyMetadata
    {
        CoerceValueCallback = CoerceCallback,
        DefaultValue = "default value",
        PropertyChangedCallback = ChangedCallback
    });

を呼び出すことと同じです。

var property = DependencyProperty.RegisterAttached(
    name: "MyProperty",
    propertyType: typeof(object),
    ownerType: typeof(MyClass),
    defaultMetadata: new PropertyMetadata
    {
        DefaultValue = "default value",
    });
property.OverrideMetadata(
    forType: typeof(MyClass),
    typeMetadata: new FrameworkPropertyMetadata
    {
        CoerceValueCallback = CoerceCallback,
        DefaultValue = "default value",
        PropertyChangedCallback = ChangedCallback
    });

結論

通常の依存性プロパティとアタッチされた依存性プロパティの間の重要な(そして唯一の)違いは、デフォルトのメタデータが DependencyProperty.DefaultMetadata プロパティで利用できるデフォルトのメタデータです。これは 備考 セクションを作成します。

非アタッチドプロパティでは、このプロパティによって返されるメタデータタイプは、以下の派生タイプにキャストすることはできません。 プロパティメターデータ タイプにキャストすることはできません。元々登録されていたメタデータを、そのオリジナルの派生メタデータ・タイプも含めて利用したい場合は GetMetadata(Type) を呼び、パラメータとしてオリジナルの登録タイプを渡します。

アタッチされたプロパティの場合、このプロパティによって返されるメタデータのタイプは、オリジナルの RegisterAttached 登録メソッドで指定されたタイプと一致します。

これは提供されたコードにはっきりと現れています。小さなヒントは、登録メソッドにも隠されています。 RegisterAttached の場合、メタデータ・パラメータは defaultMetadata という名前になりますが、一方 Register という名前になります。 typeMetadata . アタッチドプロパティでは、提供されたメタデータがデフォルトのメタデータになります。しかし、通常のプロパティの場合、デフォルトのメタデータは常に新しいインスタンスの PropertyMetadata のみで構成されます。 DefaultValue だけが設定されています (提供されたメタデータから、あるいは自動的に)。への呼び出しのみです。 OverrideMetadata の呼び出しだけが、提供されたメタデータを実際に使用します。

結果

主な実用上の違いは、通常のプロパティの場合は CoerceValueCallbackPropertyChangedCallback が該当します。 のみ はオーナー型として宣言された型から派生した型に対して適用され、 アタッチされたプロパティに対しては すべて 型に適用されます。例えば、このシナリオの場合

var d = new DependencyObject();
d.SetValue(SomeClass.SomeProperty, "some value");

登録されている PropertyChangedCallback と呼ばれることになります。 がアタッチド・プロパティとして登録された場合、しかし は呼び出されません。 と呼ばれることはありません。同じことが CoerceValueCallback .

二次的な違いは OverrideMetadata は供給された型が DependencyObject . 実際には、通常のプロパティのオーナータイプは を導出しなければなりません。 から DependencyObject であるのに対し、アタッチドプロパティでは のいずれかになります。 型(静的クラス、構造体、列挙型、デリゲートなどを含む)であることができます。

補足

MarqueIV さんの提案以外にも、通常のプロパティとアタッチドプロパティの使い分けは XAML . つまり、通常のプロパティは暗黙的な名前構文を必要とし、attachedプロパティは明示的な名前構文を必要とするということです。これは技術的には ではありません。 ですが、実際には通常そうです。分かりやすくするために

<!-- Implicit property name -->
<ns:SomeClass SomeProperty="some value" /> 

<!-- Explicit property name -->
<DependencyObject ns:SomeClass.SomeProperty="some value" />

純粋なXAML では、これらの構文の使用を統制する唯一のルールは以下の通りです。

  • 暗黙の名前構文は、要素 の場合のみ使用できます。 この要素が表すクラスが CLR という名前のプロパティがあります。
  • 明示的な名前構文は要素で使用可能 の場合のみ 完全な名前の最初の部分によって指定されたクラスが、適切な静的な を取得します。 / セット メソッド(以下 アクセサ と呼ばれる) で、フルネームの2番目の部分と一致する名前を持つ。

これらの条件を満たすことで、バッキング依存性プロパティがregularとattachedのどちらで登録されていても、対応する構文が使用できるようになります。

今述べた誤解は、大多数のチュートリアルが (純正の Visual Studio のコード スニペットとともに) CLR プロパティを使用し、アタッチされたプロパティには get/set アクセサを使用するように指示します。しかし、同時に両方を使用することを妨げるものは何もなく、好きな構文を使用することができます。