2 - DataContextを利用したバインディング
About
この項目ではDataContextを利用したバインディングについて解説します。この項目はこの前の項目1の内容を引き継ぐので参照順には注意してください。
DataContextとは
先の項目1ではTextBoxもCheckBoxは同じコントロール上に配置されていました。そして同じデータ(SimpleClassのインスタンス)をバインディングするにも関わらず、それぞれバインディング(Source)の設定を行っていました。
//TextBox ver. Binding textboxBinding = new Binding("StringProperty") { Source = this.SimpleClassData }; textBox.SetBinding(TextBox.TextProperty, textboxBinding); //CheckBox ver. Binding checkboxBinding = new Binding("BoolProperty") { Source = this.SimpleClassData }; checkBox.SetBinding(CheckBox.IsCheckedProperty, checkboxBinding);
この例のようなパターンは、実用的なレベルの実装ではあまり見かけません。なぜならば、あるコントロールが扱うデータは、1つだけであることが多いからです。オブジェクト指向が積極的に取り入れられるような設計環境下ではほとんどの場合がそうでしょう。
何度もBinding.Sourceを設定することは効率が悪く、複数回同じコードを書き続けることはトラブルを誘発します。そこで同一コントロール内であつかう単一のデータを設定し、それをBinding.Sourceとすることでこの問題を解決します。
WPFではDataContextプロパティによってこの問題を解決することができます。DataContextプロパティは、それが設定されたコントロール内の要素全てのDataContextプロパティに値が継承されます。
DataContextとバインディングの設定
バインディングするデータは先と同じです。DataContextの設定は、そのデータをバインディングするコントロールが全て含まれるコントロールに対して設定します。つまりここでは(Main)Windowです。MainWindowにはStackPanelが含まれていて、さらにそのStackPanelにはTextBoxとCheckBoxが含まれています。したがって、MainWindowのDataContextプロパティに値を設定すると、その子要素であるStackPanel、TextBox、CheckBoxの全てから設定された値を参照することができる、ということになります。
//Bindingするデータを作る this.SimpleClassData = new SimpleClass(); this.SimpleClassData.BoolProperty = true; this.SimpleClassData.StringProperty = "Hello."; //DataContextを設定する this.DataContext = this.SimpleClassData; //TextBox ver. textBox.SetBinding(TextBox.TextProperty, new Binding("StringProperty")); //CheckBox ver. checkBox.SetBinding(CheckBox.IsCheckedProperty, new Binding("BoolProperty"));
TextBoxとCheckBoxに対するバインディングはより省略した記述にしました。ここで注目したいのはBinding.Sourceプロパティを設定していない点です。Binding.Sourceが設定されていないBindingが設定された場合、バインディングはDataContextの値を参照します。したがって、DataContextに設定された値に対象となるプロパティ名が存在すれば、バインディングが成立します。
ここではDataContextにSimpleClassのインスタンスを設定し、そのプロパティStringPropertyを対象にBindingを設定したので、バインディングが成立しています。
実行と確認
項目1のサンプルと同様に、実行してバインディングができているかを確認します。DataContextを利用したバインディングは、バインディングする対象が増えるほど強力に働きます。例えば、バインディングするデータを変更する場合に、バインディングが施されていた全てのコントロールのBinding.Sourceを設定しなおすことは大きな手間です。もしかしたら設定の変更を忘れるなどのトラブルも発生させるかもしれません。
ここで、サンプル2ではButton(とそのイベントハンドラ)をもう1つ増やしてあります。
private void Button_Click1(object sender, RoutedEventArgs e) { Console.WriteLine(this.SimpleClassData.StringProperty); Console.WriteLine(this.SimpleClassData.BoolProperty); } private void Button_Click2(object sender, RoutedEventArgs e) { this.SimpleClassData.StringProperty = "From Codebehind."; this.SimpleClassData.BoolProperty = true; }
2つ目のButtonは、バインディングしたデータ=DataContextに与えたデータであるSimpleClassのプロパティの値を変更します。このButtonをクリックして値の変更を実行してみてください。すぐにわかると思いますが、TextBoxやCheckBox上に表示される値は、Buttonを実行する前の値からそのまま変更されません。続けて1つ目のButtonを押してSimpleClassの値を出力して見ます。するとButton2でソースコードから変更された値が出力されることが確認できます。
このように、普通にバインディングを利用して、ソースコード側からその値を変更した場合には、インターフェース上の値が変更されることはありません。そこで次の項目ではソースコード側からのデータの変更をインターフェースに通知するようにし、お互いにデータの変更がフィードバックされるような例をあつかいます。