読者です 読者をやめる 読者になる 読者になる

がりらぼ

WindowsRuntimeの応援ブログ

添付プロパティと添付ビヘイビアを解説してみる

WindowsRuntime XAML

添付プロパティと添付ビヘイビアとはなんぞや?という疑問に、復習がてら解説していこうと思います。

添付プロパティ

まず添付プロパティ 添付プロパティとはその名の通り今ある既存のコントロールに自分で新しくつくったプロパティを添付することです。

たとえばButtonコントロールが3つあるこのようなUIで

f:id:garicchi:20140327032142p:plain

「このボタンは3つの内、真ん中に位置している」という情報を付け加えたいとします。 ようするにButtonにIsCenterたるプロパティがあれば解決するのですが残念ながら無い。

一つ目の解決策としては、Buttonコントロールを継承してIsCenterプロパティを生やせば解決できます。

f:id:garicchi:20140327032557p:plain

もう一つの解決策として添付プロパティがあります。 添付プロパティはわざわざコントロールを継承しなくてもプロパティを外付けできるのでButtonコントロールにあとからIsCenterプロパティを外付けできるすぐれものです。

やり方としては、まず新しくクラスを作り、 「propa」と入力し、「Tabを2回」押します。

f:id:garicchi:20140327032820p:plain

するとこのようにコードスニペットによって自動生成されて便利です。

f:id:garicchi:20140327032923p:plain

そして、添付したいプロパティの型と名前を変更します。

public class AttachedProperty
{

    public static bool GetIsCenter(DependencyObject obj)
    {
        return (bool)obj.GetValue(IsCenterProperty);
    }

    public static void SetIsCenter(DependencyObject obj, bool value)
    {
        obj.SetValue(IsCenterProperty, value);
    }
    //プロパティの名前、プロパティの型、プロパティを所有するクラスの型、デフォルト値
    public static readonly DependencyProperty IsCenterProperty =
        DependencyProperty.RegisterAttached("IsCenter", typeof(bool), typeof(AttachedProperty),new PropertyMetadata(false));

    
}

あとはXAMLで添付したいコントロールに添付するだけです。

<Button x:Name="button1"  local:AttachedProperty.IsCenter="false" Content="button1"  Margin="539,138,0,0"/>
<Button x:Name="button2"  local:AttachedProperty.IsCenter="true" Content="button2"  Margin="539,292,0,0" />
<Button x:Name="button3"  local:AttachedProperty.IsCenter="false" Content="button3"  Margin="539,435,0,0"/>

これで既存のButtonコントロールにIsCenterというbool型のプロパティを添付することができました。

ちなみにC#コードから添付プロパティを取得、設定するにはこうします。

//添付プロパティの値を取得
bool isCenter=AttachedProperty.GetIsCenter(this.button1);
//添付プロパティの値を設定
AttachedProperty.SetIsCenter(this.button1,true);

添付ビヘイビア

続いて添付ビヘイビアです。 添付ビヘイビアとはビヘイビア(何かが起こった時の振る舞い)を既存のコントロールに添付することができるものです。

たとえば先程のボタンたち

f:id:garicchi:20140327032142p:plain

このボタン中央だったときそのボタンのContentを「ヨドバシカメラ」に変える方法を考えましょう。

一番単純な方法はClickイベントでContentプロパティを変えることですね。

private void button1_Click(object sender, RoutedEventArgs e)
{
    this.button1.Content = "ヨドバシカメラ";
}

しかし、例えばButton2、Button3も同じような挙動をしてほしいと考えます。 するとButton2とButton3にも同じようにClickイベントを書いてやらなければいけません。 これでは再利用性の糞も無いので添付ビヘイビアというやつを使います。

添付ビヘイビアとは、添付プロパティが変更された時の挙動を添付するというものです。 添付ビヘイビアの作り方は添付プロパティと同じようにまず新しくクラスを作成してprop→Tab2回を押します。

この時のプロパティの名前や型は何でもいいです。

public class AttachedProperty
{

    public static bool GetIsCenter(DependencyObject obj)
    {
        return (bool)obj.GetValue(IsCenterProperty);
    }

    public static void SetIsCenter(DependencyObject obj, bool value)
    {
        obj.SetValue(IsCenterProperty, value);
    }

    public static readonly DependencyProperty IsCenterProperty =
        DependencyProperty.RegisterAttached("IsCenter", typeof(bool), typeof(AttachedProperty),new PropertyMetadata(false));

    
}

この時、「new PropertyMetadata()」というところ、つまりPropertyMetadataクラスのコンストラクタは第二引数を設定することができて、第二引数にはそのプロパティが変更された時のデリゲートを設定することができます。

なのでこのようにPropertyMetadataの第二引数にコールバック関数を生やしましょう。

public static bool GetIsCenter(DependencyObject obj)
{
    return (bool)obj.GetValue(IsCenterProperty);
}

public static void SetIsCenter(DependencyObject obj, bool value)
{
    obj.SetValue(IsCenterProperty, value);
}

public static readonly DependencyProperty IsCenterProperty =
    DependencyProperty.RegisterAttached("IsCenter", typeof(bool), typeof(AttachedProperty),new PropertyMetadata(false,PropertyChanged));

public static void PropertyChanged(DependencyObject s,DependencyPropertyChangedEventArgs e)
{

}

あとはプロパティが変更されたときに、変更された値を見て、どのような挙動をするかを書けばオッケーです

    public class AttachedProperty
    {

        public static bool GetIsCenter(DependencyObject obj)
        {
            return (bool)obj.GetValue(IsCenterProperty);
        }

        public static void SetIsCenter(DependencyObject obj, bool value)
        {
            obj.SetValue(IsCenterProperty, value);
        }

        public static readonly DependencyProperty IsCenterProperty =
            DependencyProperty.RegisterAttached("IsCenter", typeof(bool), typeof(AttachedProperty),new PropertyMetadata(false,PropertyChanged));

        public static void PropertyChanged(DependencyObject s,DependencyPropertyChangedEventArgs e)
        {
            if ((bool)e.NewValue == true)
            {
                Button button = s as Button;
                button.Content = "ヨドバシカメラ";
            }

        }
    }

XAMLはButtonを3つ用意して真ん中のボタンのIsCenter添付プロパティをtrueに

<Button x:Name="button1" Content="button1" local:AttachedProperty.IsCenter="false" Margin="539,138,0,0"/>
<Button x:Name="button2" Content="button2"  local:AttachedProperty.IsCenter="true"  Margin="539,292,0,0" />
<Button x:Name="button3" Content="button3" local:AttachedProperty.IsCenter="false"  Margin="539,435,0,0"/>

f:id:garicchi:20140327040828p:plain

さらに変更コールバック関数内でイベント登録することによってイベントに対する挙動を設定できるのでよりビヘイビアっぽくなります。 ※ただしデフォルト値と違う値をXAMLで設定しないとコールバック関数が呼ばれません。

        public static void PropertyChanged(DependencyObject s,DependencyPropertyChangedEventArgs e)
        {
            Button button = s as Button;
            button.Click += (ss, ee) =>
            {
                button.Content = "ヨドバシカメラ";
            };
            

        }

まあ、添付プロパティにしろ、添付ビヘイビアにしろ、外付けをすることによって再利用性が高まるというところがいいですね。