前回からちょっと間があいてるけど、思い出しながらやってみようと思う。
確か前回は、INotifyPropertyChangedインターフェースとかについて書いたような気がする。
そのときに、INotifyPropertyChangedインターフェースを実装して、適切にプロパティのsetに変更を通知するコードを書けば、プロパティが書き換わったときにBindingした先の値も書き換わってくれる。とかいう感じだったと思う。
今回は、Bindingの値の書き換えとかのタイミングや方向についてちょびっと実験してみる。
とりあえず、いつも通りPersonクラスを作成する。ここらへんまでは問題ない。前回やったINotifyPropertyChangedインターフェースも実装して、Bindingに備える。
using System.ComponentModel;
namespace WpfBinding3
{
public class Person : INotifyPropertyChanged
{
#region INotifyPropertyChanged メンバ
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
private string _name;
public string Name
{
get
{
return _name;
}
set
{
if (_name == value)
{
return;
}
_name = value;
OnPropertyChanged("Name");
}
}
}
}
画面を作りに入る前に、今回のやつをやるために使う言葉をちょびっと説明。
上の図は、Bindingの雰囲気を図にしてみたものになる。ここで重要なのは、ターゲットとソースという言葉。
ソースは、今回の例でいうとPersonクラスのオブジェクトにあたるもの。ターゲットは、TextBlockやTextBoxみたいなWPFのコントロールになる。
これを頭に入れたら、さくっと簡単なサンプルをこさえる。
まず、DataContextにPersonクラスのオブジェクトを入れる。
んで、それとBindingするTextBoxを用意してTextプロパティとPersonのNameプロパティをバインドする。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace WpfBinding3
{
public partial class Window1 : Window
{
private Person _person;
public Window1()
{
InitializeComponent();
_person = new Person { Name = "田中 太郎" };
DataContext = _person;
}
}
}
<Window x:Class="WpfBinding3.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<Grid>
<TextBox Grid.Row="0" Grid.Column="1" Text="{Binding Path=Name}" />
</Grid>
</Window>
これを実行すると、田中 太郎さんが表示される。
これに、ボタンを1つ追加してボタンのクリックイベントでPersonのNameを田中 一郎に書き換えるコードを書いてみる。
<Window x:Class="WpfBinding3.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBox Grid.Row="0" Grid.Column="0" Text="{Binding Path=Name}" />
<Button Content="田中 一郎化計画発動" Click="Button_Click" />
</Grid>
</Window>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace WpfBinding3
{
public partial class Window1 : Window
{
private Person _person;
public Window1()
{
InitializeComponent();
_person = new Person { Name = "田中 太郎" };
DataContext = _person;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
_person.Name = "田中 一郎";
}
}
}
これを実行すると、前回確認したのと同じように、Personオブジェクトの変更がバインド先へ通知されて、TextBoxが田中 一郎になる。
実行直後
ボタンクリック直後
これで、やっと今日のスタートラインに立てた!!
んじゃ、これをベースに改造していく。
BindingのMode
さて、モードです。
モードっていうのは、Bindingがどんな風に振舞うかを決めるものでModeプロパティで指定できる。
Modeプロパティの値はBindingMode列挙の値で、全部で5種類もある。
5種類を全部挙げてみる。
- Default
何も指定しないとこれになる。TextBoxみたいな編集可能な奴はTwoWay的な動きをする。そうじゃないTextBlockみたいな編集不可なものはOneWay的な動きをする。
- OneTime
最初の一回のみターゲットの値をソースからもってくる。最初の1回というのを厳密に言うと、アプリ起動時かDataContextの変更時。
- OneWay
ソースの変更をターゲットに通知する。それだけ。逆はしない。
- OneWayToSource
OneWayの逆。ターゲットの変更をソースに通知する。それだけ。逆はしない。
- TwoWay
どっちの変更も通知しあう。
ということで、5つのTextBoxに各々Modeを設定してみようと思う。
XAMLをさくっといじくるとこんな感じになる。
1(ひー!)Default!
<Window x:Class="WpfBinding3.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBox Grid.Row="0" Grid.Column="0" Text="{Binding Path=Name, Mode=Default}" />
<Button Grid.Row="1" Grid.Column="0" Content="田中 一郎化計画発動" Click="Button_Click" />
</Grid>
</Window>
Bindingの部分にPathに加えてModeを足してるのが今までと違う!
違うけど、何も指定しないとDefaultを指定したのと同じなので実行結果は同じになる。
同じなので省略。
次!
2(ふー!)OneTime
<TextBox Grid.Row="0" Grid.Column="0" Text="{Binding Path=Name, Mode=OneTime}" />
これは、最初にPersonからTextBoxに値が渡されておしまいなので、ボタンを押しても田中 一郎化作戦は失敗する。
実行直後
ボタンを押しても変化無し
ここで気づいた。Personオブジェクトの値を確認するものを用意してなかった。急遽ボタンを1つ追加。
<Window x:Class="WpfBinding3.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBox Grid.Row="0" Grid.Column="0" Text="{Binding Path=Name, Mode=OneTime}" />
<Button Grid.Row="1" Grid.Column="0" Content="田中 一郎化計画発動" Click="Button_Click" />
<Button Grid.Row="2" Grid.Column="0" Content="だんぷ" Click="Button_Click_1" />
</Grid>
</Window>
ボタンクリックは、下のような感じ。
private void Button_Click_1(object sender, RoutedEventArgs e)
{
Debug.WriteLine("Person Name: " + _person.Name);
}
さて気を取り直して実行!!
実行直後
一郎化計画発動
一郎化できてるかだんぷ
Person Name: 田中 一郎
というわけで、一回こっきりなのです。
3(みー!)OneWay
一方通行です!!どんな動きをするか実験です。
<TextBox Grid.Row="0" Grid.Column="0" Text="{Binding Path=Name, Mode=OneWay}" />
実行して田中一郎化計画発動
テキストボックスの値を書き換えてだんぷをクリック
Person Name: 田中 一郎
というわけで、テキストボックスでの変更はPersonオブジェクトへ伝わらなくなる。
4(よー!)OneWayToSource
OneWayToSourceに設定してみた。
<TextBox Grid.Row="0" Grid.Column="0" Text="{Binding Path=Name, Mode=OneWayToSource}" />
起動直後は下のような感じ。いきなり他のと違う。
これは多分、バインドされたときにターゲットからソースに値がわたったせいだと思う。
ターゲット(テキストボックス)のTextプロパティは空文字だからね。
証拠にだんぷをクリックすると下のように表示される。
Person Name:
ターゲットからソースには値が伝わるのでテキストボックスの値を書き換えるとちゃんとPersonのNameも変わる。
テキストボックス書き換えてだんぷをクリック
Person Name: 田中 一郎
というわけで、一方通行でした。
5(いつ!)TwoWay
というわけでTowWayです。
<TextBox Grid.Row="0" Grid.Column="0" Text="{Binding Path=Name, Mode=TwoWay}" />
因みに、これもデフォの動きと同じ。
というわけで動作も割愛!
WPFのバインドは、この5つの動きをうまいこと使い分けていきましょう。
どういう時にどういうものを使うのかは謎だ。
誰かまとめてくれたりしてないかな?
WPF実装パターンみたいな。