UserControlのプロパティに値をBindingする

BindingとUserControlはとても便利なのであわせてWPアプリ開発でも使いたいところ。詰まったところをメモしておきます。どうでもよいですがWindows関係は、.NETで使えるのかSilverlightで使えるのかStore appで使えるのかPhoneで使えるのかぱっと見分からない情報が多くて困りますね。なおWP8に関する日本語の情報はほぼ存在しない模様。

Binding

やり方はいろいろあるようですが、UIElementのDataContextにオブジェクトをセットする方法を使っています。DataContextが内外から変更できてしまうからイマイチとかいう向きもあるようですが、楽なので。

pageのXAMLに置いてあるButtonやらRectangleやらのプロパティに、設定したい値を持ってるINotifyPropertyChangedなオブジェクトのプロパティをセットして、値の更新をUIに即時反映するようなアレです。らくちん。多用しまくるとパフォーマンス大丈夫なのかなと心配になりますが、ユーザが新しい端末を買い続けてくれれば問題ありません。詳しいことは意味の分からないMSDNのページをどうぞ。

Buttonとか、組み込みのUIElementたちにはこれで好きな値をBindingすればいいだけなので、弊プロジェクトでは多用しています。

よくわからなくなったので(後述)、サンプルアプリを作って試しました。

https://github.com/naotaco/BindingTest

Binding on UserControl

UserControlは特定のUI部品を作りたいときに超便利なのでしょっちゅう使ってます。だんだんエスカレートして、組み込みのUIElementみたいにプロパティにBindingさせろよという気になってきたのでちょっと調べました。

まず、Bindingなしで、固定値をpageのXAMLから設定するときは、UserControlのプロパティをpublicにしてあげればそれだけでOKです。

 

    public partial class MyBrandNewSlider : UserControl
    {
    ...
        public bool Checked { get; set; }

    ...
<phone:PhoneApplicationPage
    xmlns:controls="clr-namespace:BindingTest.Control"
    ...
    <controls:CheckboxControl Name="ControlCheckbox" Checked="false" />

    ...
 

 

こんなかんじで。

だがうきうきして下記みたいにプロパティにBindingを使ったらExceptionが起きて死んだ。

 

<phone:PhoneApplicationPage
    xmlns:controls="clr-namespace:BindingTest.Control"
    ...
    <controls:CheckboxControl Name="ControlCheckbox" Checked="{Binding Checked, Mode=TwoWay}" />

    ...
 

ここでBindingしてるのは値を抱えてるINotifyPropertyChangedを実装したオブジェクトのプロパティで、このオブジェクトは既に親のUIElementのDataContextにセットされてる。UserControlにBindingしたときだけ落ちる…

で、調べて見つけた今回の参考文献:http://www.geekchamp.com/tips/how-to-expose-properties-of-a-user-control-in-windows-phone

言われるがままに、UserControlにコードを足す。

 


 public bool Checked
 {
     /* get; set; このままだとNG. 末尾参照のこと。 */
    get { return (bool)GetValue(CheckboxProperty); }
    set
    {
        SetValue(CheckboxProperty, value);
        _CheckBox.IsChecked = value;
    }
 }

 public static readonly DependencyProperty CheckboxProperty = DependencyProperty.Register(
      "Checked",
      typeof(bool),
      typeof(CheckboxControl),
      new PropertyMetadata(false, new PropertyChangedCallback(CheckboxControl.StateChanged)));

 private static void StateChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
 {
     (d as CheckboxControl).Checked = (bool)e.NewValue;
 }

 ...

こんなかんじ。

こうするとDataContextのプロパティが変わったらUserControlに用意したコールバックが呼ばれます。なんで自分でセットしなきゃならんのじゃという気もするが。(なにかいい方法ないのか)

誤りとかもっとよい方法などあったら教えてくださいませ。

 


追記(2014/11/09)

初出時、変更後もプロパティを get; set; のままとしていましたが、下記コメントをいただきました。

 

試してみたところ、おっしゃるようにTwowayでバインドするとうまく動きませんでした(Controlのプロパティの変更がDataContextのプロパティに伝わらない)。ありがとうございます!