7.2 暗黙のデータソース
7.2.1 データソースの共有
通常、何らかのデータ構造を表示、作成、または編集するフォーム上のコントロールは、データの個々のプロパティをソースとします。それぞれのコントロールは、個々のプロパティにバインディングするためのオブジェクトが設定されますが、プロパティを保有するデータソースとなるオブジェクトは共有するのが一般的でしょう。
この場合、個々のバインディングオブジェクトの Source プロパティにソースとなるオブジェクトを設定するのは冗長です。FrameworkElement は、バインディングのデータソースとなるオブジェクトを設定するための DataContext プロパティを公開しています。
[LocalizabilityAttribute(LocalizationCategory.NeverLocalize)] public Object DataContext { get; set; }
DataContext プロパティには、データソースを表すオブジェクトを設定します。設定された Binding オブジェクトがデータソースを提供しない場合、コントロールは DataContext プロパティに保存されているオブジェクトをデータソースとして参照します。
さらに、コントロールは自分自身の DataContext プロパティにデータソースが設定されていない場合、親コントロールの DataContext を検索します。そのため、複数のパネルにネストされた複雑なレイアウトのフォームでも、最上位のパネルの DataContext にオブジェクトを設定することで、容易にデータソースを共有することができます。
DataContext をデータソースとして利用する場合、Binding オブジェクトを明示的に生成することなくバインディングを実現することも可能です。FrameworkElement クラスには、オーバーロードされたもう 1 つの SetBinding() メソッドが用意されています。
public BindingExpression SetBinding ( DependencyProperty dp, string path )
dp パラメータに、バインディングターゲットとなる依存プロパティを指定するところまでは同じですが、この SetBinding() メソッドは、バインディングソースのプロパティを、path パラメータから文字列型で指定することができます。
using System; using System.ComponentModel; using System.Windows; using System.Windows.Data; using System.Windows.Controls; class User : INotifyPropertyChanged { private string name, address; public event PropertyChangedEventHandler PropertyChanged; public string Name { set { name = value; OnPropertyChanged("Name"); } get { return name; } } public string Address { set { address = value; OnPropertyChanged("Address"); } get { return address; } } protected void OnPropertyChanged(string name) { if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(name)); } } class Test : Window { [STAThread] public static void Main() { Window wnd = new Test(); Application app = new Application(); app.Run(wnd); } private User user; private Label nameLabel, addrLabel; private TextBox nameTextBox, addrTextBox; public Test() { user = new User(); nameLabel = new Label(); nameLabel.SetBinding(ContentControl.ContentProperty, "Name"); addrLabel = new Label(); addrLabel.SetBinding(ContentControl.ContentProperty, "Address"); nameTextBox = new TextBox(); nameTextBox.Margin = new Thickness(10); nameTextBox.Text = "名前を入力してください"; nameTextBox.TextChanged += nameTextBoxTextChanged; addrTextBox = new TextBox(); addrTextBox.Margin = new Thickness(10); addrTextBox.Text = "住所を入力してください"; addrTextBox.TextChanged += addrTextBoxTextChanged; StackPanel panel = new StackPanel(); panel.Children.Add(nameLabel); panel.Children.Add(addrLabel); panel.Children.Add(nameTextBox); panel.Children.Add(addrTextBox); panel.DataContext = user; Content = panel; } private void nameTextBoxTextChanged(Object sender, TextChangedEventArgs e) { user.Name = nameTextBox.Text; } private void addrTextBoxTextChanged(Object sender, TextChangedEventArgs e) { user.Address = addrTextBox.Text; } }
コード1の User クラスは、ユーザー個人を表すためのクラスであると仮定し、この場では仮に名前を表す Name プロパティと住所を表す Address プロパティを公開しています。GUI コントロールでこのデータを扱うには、それぞれのプロパティの値を表示するラベルを作成したり、データを入力するためのテキストボックスを表示させることになるでしょう。
このプログラムでは、データを表示する 2 つの Label オブジェクトをバインディングターゲットとしています。しかし、個々の Label オブジェクトのために明示的に Binding オブジェクトを作成し Source プロパティにデータソースを設定するコードは記述していません。表示するコントロールの親パネルである StackPanel オブジェクトの DataContext に User オブジェクトを設定しているため、暗黙的にこのオブジェクトがデータソースとして認識されます。