<オブジェクトとのバインディング>
今度は、コントロール同士ではなくコードビハインド側で作成したオブジェクトとコントロールをバインドしてみる。
とりあえず、画面表示用に以下のXAMLを定義した。
<Window x:Class="BindingSample.Window2"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window2" Height="300" Width="300" Name="MyWindow">
<StackPanel>
<StackPanel Orientation="Horizontal">
<Label Content="名前:"/>
<TextBox Text="{Binding Name}" />
</StackPanel>
<StackPanel Orientation="Horizontal">
<Label Content="住所:"/>
<TextBox Text="{Binding Address}" />
</StackPanel>
</StackPanel>
</Window>
バインドするエンティティのクラスはとりあえず以下のとおり。
class Person
{
public string Name { get; set; }
public string Address { get; set; }
}
あとはコードビハインドで、このクラスのインスタンスを作り、WindowのContextに設定してあげれば良い。
public Window2()
{
InitializeComponent();
Person p = new Person { Name = "原 辰徳", Address = "相模原市1-1" };
this.MyWindow.DataContext = p;
}
バインド先のTextBoxは自分の親を直近から探していって最初に見つかったDataContextを使う。(包含継承というらしい)
コントロールの時と同様にコードだけでバインドすると以下のようになる。
public Window2()
{
InitializeComponent();
Person p = new Person { Name = "原 辰徳", Address = "相模原市1-1" };
//this.MyWindow.DataContext = p;
var bindName = new Binding();
bindName.Path = new PropertyPath("Name");
bindName.Source = p;
txtName.SetBinding(TextBox.TextProperty, bindName);
var bindAddr = new Binding();
bindAddr.Path = new PropertyPath("Address");
bindAddr.Source = p;
txtAddr.SetBinding(TextBox.TextProperty, bindAddr);
}
9行目と14行目はDataContextを指定すれば不要になる。
また、Bindingクラスのコンストラクタ引数にPathを文字列で指定できるようなので8行目と13行目も省略できることになる。
public Window2()
{
InitializeComponent();
Person p = new Person { Name = "原 辰徳", Address = "相模原市1-1" };
this.MyWindow.DataContext = p;
var bindName = new Binding("Name");
txtName.SetBinding(TextBox.TextProperty, bindName);
var bindAddr = new Binding("Address");
txtAddr.SetBinding(TextBox.TextProperty, bindAddr);
}
少し簡単になった。
ちなみに、バインドモードは指定しなければDefaultという値になり状況に応じてWPFが決めてくれるらしい。TextBoxの様に入力可能なコントロールとバインドした場合は双方向になるとのこと。
ただし、TextBoxは依存関係プロパティになっていて変更通知機能があるのでTextBoxの変更をDataContextに伝えることができるが、DataContextへの変更はそのままではTextBoxに伝わらないのだと。
一般的にはDataContextに指定されたCLRオブジェクトに対してはINotifyPropertyChangedインタフェースを実装することにより変更通知を行う必要がある。
とりあえずDataContextの値を確認する為にボタンを追加してソースを以下の様に変更した。
public partial class Window2 : Window
{
Person p = new Person { Name = "原 辰徳", Address = "相模原市1-1" };
public Window2()
{
InitializeComponent();
this.MyWindow.DataContext = p;
var bindName = new Binding("Name");
txtName.SetBinding(TextBox.TextProperty, bindName);
var bindAddr = new Binding("Address");
txtAddr.SetBinding(TextBox.TextProperty, bindAddr);
}
//DataContextの値を確認
private void Button_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show(p.Name + ":" + p.Address,"DataContextの確認");
}
//名前を長島に
private void Button_Click_1(object sender, RoutedEventArgs e)
{
p.Name = "長島茂雄";
}
}
これで確認してみると確かにDataContextへの変更はTextBoxに反映しないことが分かった。
DataContextに設定しているPersonクラスにINotifyPropertyChangedインタフェースを実装したのが以下のソース。
PropertyChangedイベントを発せさせれば良いだけだ。
(まったく決まりきった実装になるようだ)
public class Person : INotifyPropertyChanged
{
private string _name;
private string _address;
public string Name
{
get
{
return _name;
}
set
{
if (_name == value)
{
return;
}
_name = value;
OnPropertyChanged("Name");
}
}
public string Address
{
get
{
return _address;
}
set
{
if (_address == value)
{
return;
}
_address = value;
OnPropertyChanged("Address");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
var d = PropertyChanged;
if (d != null)
{
d(this, new PropertyChangedEventArgs(propertyName));
}
}
}
確かにDataContextへの変更が即座にTextBoxへ反映されるようになる。
0 件のコメント:
コメントを投稿