かずきのBlog@hatena

すきな言語は C# + XAML の組み合わせ。Azure Functions も好き。最近は Go 言語勉強中。日本マイクロソフトで働いていますが、ここに書いていることは個人的なメモなので会社の公式見解ではありません。

Expression Blend SDKのBehaviorとかを再実装したときに覚えたこと

Blend SDKにはBehaviors添付プロパティやTriggers添付プロパティが定義されていて、そこにBehaviorやTriggerを追加していくという感じで使います。

同じようなものを作ろうとしたときに問題にぶちあたりました。添付プロパティの初期値どうしよう?
nullにするとぬるりが出るし、new BehaviorsCollection()と適当な初期値をMetadataで与えることは出来るけど、そうすると全部の添付プロパティで同じコレクションが再利用されて困ったことになりそう・・・。ということで既存の奴はどうやってるんだろうと覗いてみると、以下のようなことをしていました。

BehaviorsやTriggersという添付プロパティなんて存在しなかった

デバッガで止めてBehaviorsPropertyの中身を見てみると名前がBehaviorsという名前以外で登録されていました。でもGetBehaviorsというstaticメソッドは定義されているという状態でした。つまり、XAMLでBehaviors添付プロパティを設定するようなコードを書くとBehaviorsPropertyを使って値が取得されるのではなく、GetBehaviorsメソッドを使って値が取得されます。
ということでGetBehaviorsの中で、値がnullだったら作ってやって、BehaviorsPropertyを使ってDependencyObjectに値をセットしてやればいけそうです。書いててなんだかよくわからなくなってきたので、コードを書いておきます。

// Behaviorsという添付プロパティと見せかけて実は違う名前で登録しておく
public static readonly DependencyProperty BehaviorsProperty =
    DependencyProperty.RegisterAttached(
        "ShadowBehaviors",
        typeof(BehaviorCollection),
        typeof(Interaction),
        new PropertyMetadata(
            null,
            BehaviorsChanged));
// XAMLでBehaviors添付プロパティを取得しようとすると、このBehaviorsPropertyが使われないで
// こっちのGetBehaviorsが使われる。
// (BehaviorsPropertyは別名で登録されているのでstaticメソッドのほうが優先される)
public static BehaviorCollection GetBehaviors(DependencyObject obj)
{
    // BehaviorsPropertyを使って値を取得してみる
    var ret = (BehaviorCollection)obj.GetValue(BehaviorsProperty);
    // 値がnullだったらインスタンスを作ってセットしておく
    if (ret == null)
    {
        ret = new BehaviorCollection();
        obj.SetValue(BehaviorsProperty, ret);
    }
    return ret;
}

気づくまで悩んだのでメモ。ってかこのテクニック使うこと他であるのかなぁ・・・。