ページ

2011年11月13日日曜日

◆WPF バインディング2

<オブジェクトとのバインディング>

今度は、コントロール同士ではなくコードビハインド側で作成したオブジェクトとコントロールをバインドしてみる。

とりあえず、画面表示用に以下の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>

image


バインドするエンティティのクラスはとりあえず以下のとおり。


    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を使う。(包含継承というらしい)


image


コントロールの時と同様にコードだけでバインドすると以下のようになる。


        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の値を確認する為にボタンを追加してソースを以下の様に変更した。


image


    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へ反映されるようになる。


image

0 件のコメント:

コメントを投稿

私が最近チェックした記事