かずきのBlog@hatena

日本マイクロソフトに勤めています。このブログは個人ブログなので、ここに書いている内容は個人的な意見で会社の公式見解ではない点にご注意ください。好きなものは XAML と C# 。苦手なものは型の無い言語です。

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;
}

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