メモの日々


2022年01月24日(月) [長年日記]

[c#] XAMLの添付プロパティ

依存関係プロパティと似たものに添付プロパティがある。

ここに

添付プロパティは XAML の概念であり、依存関係プロパティは WPF の概念です。

とある。なるほど? XAMLのドキュメントで添付プロパティに言及しているのはこの辺りだろうか。

添付プロパティの例

添付プロパティとは、次のようなXAML片の「local:Hello.Prop1」のことだ。

<Button x:Name="Button1" local:Hello.Prop1="こんにちは" />

Buttonオブジェクトに、元々は無いProp1というプロパティを「添付」しているということだろう。

このような添付プロパティは次のようなクラスを作れば使えるようになる。

    public class Hello
    {
        public static string GetProp1(DependencyObject target) => "hello";
        public static void SetProp1(DependencyObject target, string value) {}
    }

これで任意のDependencyObjectに対してXAML上でHello.Prop1をプロパティとして添付できるようになる。

ただし、上のHelloクラスは常に"hello"を返すだけなので、プロパティとしては不完全だ。

添付プロパティのDependencyPropertyを使った実装

WPFのDependencyPropertyは添付プロパティを実装するようにも設計されている。DependencyPropertyを使うことでバインディングなどにも対応できるようになる。

Helloクラスを次のように変更する。

    public static class Hello
    {
        public static readonly DependencyProperty Prop1Property =
            DependencyProperty.RegisterAttached(
                "Prop1",
                typeof(string),
                typeof(Hello),
                new PropertyMetadata(defaultValue: "hello"));

        public static string GetProp1(DependencyObject target) =>
            (string)target.GetValue(Prop1Property);

        public static void SetProp1(DependencyObject target, string value) =>
            target.SetValue(Prop1Property, value);
    }

こうすると、最初のXAML片にあるボタンを参照する変数button1に対し

button1.GetValue(Hello.Prop1Property)

が "こんにちは" を返すようになる。

  • Helloクラスのインスタンスは不要なのでstaticクラスにした。
  • 依存関係プロパティを実装する際にはDependencyProperty.Register()を使うが、添付プロパティを実装する際にはDependencyProperty.RegisterAttached()を使う。両者の違いはメタデータが引数のownerType以外の型に対しても作用するかどうか。DependencyProperty.Register() or .RegisterAttached()にある回答が参考になる。
  • RegisterAttached()に指定するownerTypeは何でもいいような気がするが、nameとownerTypeが同じプロパティを複数登録することはできない(コンパイルはできるが実行時にエラーになる)ので、衝突を防ぐために自クラスの型を指定すべきなのだと思う。