【お勉強記録】WPFデータバインディング3
コレクションのバインディング
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace _0721Study { class UserCollenction { public UserCollenction() { User user1 = new User(); user1.Name = "山田太郎"; user1.Birthday = new DateTime(1990, 7, 21); user1.Adress = "東京都中央区"; this.users.Add(user1); User user2 = new User(); user2.Name = "田中次郎"; user2.Birthday = new DateTime(1985, 11, 15); ; user2.Adress = "福岡県福岡市博多区"; this.users.Add(user2); } public List<User> Users { get { return users; } } private List<User> users = new List<User>(); } }
<Window x:Class="_0721Study.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:_0721Study" Title="MainWindow" Height="350" Width="525"> <Window.Resources> <local:UserCollenction x:Key="oUserCollenction"/> </Window.Resources> <StackPanel Margin="10" DataContext="{Binding Source={StaticResource oUserCollenction}}"> <ListBox ItemsSource="{Binding Path=Users}" > <ListBox.ItemTemplate> <DataTemplate> <Grid> <Grid.RowDefinitions> <RowDefinition /> <RowDefinition /> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="200" /> <ColumnDefinition Width="*"/> </Grid.ColumnDefinitions> <TextBlock Grid.Row="0" Grid.Column="0" Text="{Binding Path=Name}"></TextBlock> <TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding Path=Adress}"></TextBlock> </Grid> </DataTemplate> </ListBox.ItemTemplate> </ListBox> </StackPanel> </Window>
List
DataTemplateは以下のようにも書き換えられそう。
<Window x:Class="_0721Study.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:_0721Study" Title="MainWindow" Height="350" Width="525"> <Window.Resources> <local:UserCollenction x:Key="oUserCollenction"/> <DataTemplate x:Key="userTemplate"> <Grid> <Grid.RowDefinitions> <RowDefinition /> <RowDefinition /> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="200" /> <ColumnDefinition Width="*"/> </Grid.ColumnDefinitions> <TextBlock Grid.Row="0" Grid.Column="0" Text="{Binding Path=Name}"></TextBlock> <TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding Path=Adress}"></TextBlock> </Grid> </DataTemplate> </Window.Resources> <StackPanel Margin="10" DataContext="{Binding Source={StaticResource oUserCollenction}}"> <ListBox ItemsSource="{Binding Path=Users}" ItemTemplate="{StaticResource userTemplate}" > </ListBox> </StackPanel> </Window>
DataTemplateをResourcesに定義しておくことで、複数箇所で使いまわしができますね。
INotifyPopertyChanged
ソースからターゲットへの同期をするには、ソースとなるオブジェクトがINotifyPropertyChangedを実装する必要があるとのこと。 どうでもいいですが、何を実装させるかすぐにわかる素晴らしい命名ですね。
INotifyPropertyChangedは「PropertyChanged」イベントを持っていて、バインドソースに変更があった場合にターゲットに通知します。
バインディングの値の同期の方法は4つあり、そのうち「OneWay」と「TwoWay」でソースの変更されるとターゲットが再バインディングされます。
- OneWay
- TwoWay
- OneWayToSource
- OneTime
using System.ComponentModel; namespace _0721Study { class Test : INotifyPropertyChanged { public string Title { get { return title; } set { title = value; NotifyPropertyChange("Title"); } } private string title; public event PropertyChangedEventHandler PropertyChanged; protected void NotifyPropertyChange(string propertyName) { if (null != this.PropertyChanged) { PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } } } }
<Window x:Class="_0721Study.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:_0721Study" Title="MainWindow" Height="350" Width="525"> <Window.Resources> <local:Test x:Key="oTest" Title="Test"/> </Window.Resources> <StackPanel Margin="10" DataContext="{StaticResource oTest}"> <Grid> <Grid.RowDefinitions> <RowDefinition></RowDefinition> <RowDefinition></RowDefinition> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition></ColumnDefinition> <ColumnDefinition></ColumnDefinition> </Grid.ColumnDefinitions> <TextBlock Grid.Row="0" Grid.Column="0" Text="OneTime"></TextBlock> <TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding Path=Title, Mode=OneTime}"></TextBlock> <TextBlock Grid.Row="1" Grid.Column="0" Text="OneWay"></TextBlock> <TextBlock Grid.Row="1" Grid.Column="1" Text="{Binding Path=Title, Mode=OneWay}"></TextBlock> </Grid> <Grid> <Grid.RowDefinitions> <RowDefinition></RowDefinition> <RowDefinition Height="20px"></RowDefinition> </Grid.RowDefinitions> <TextBox Grid.Row="0" Text="{Binding Path=Title ,UpdateSourceTrigger=PropertyChanged}"></TextBox> </Grid> </StackPanel> </Window>
TextBlockのうちOneTimeの方は起動時にバインドされたままになっていますが、OneWayの方はTextBoxの入力に応じて、値が変わるのが確認できました。
また上の例ではTextBoxにUpdateSourceTrigger属性をPropertyChangedにしているため、テキストを入力するたびにその場で上のTextBlockの値が変わります。 これをLostFocusにするとフォーカスがほかに移動した時点で再バインドされるようになります。
<Grid> <Grid.RowDefinitions> <RowDefinition></RowDefinition> <RowDefinition Height="20px"></RowDefinition> </Grid.RowDefinitions> <TextBox Grid.Row="0" Text="{Binding Path=Title ,UpdateSourceTrigger=LostFocus}"></TextBox> <Button Grid.Row="1" Content="Focus用"></Button> </Grid>
フォーカスをボタンに移動した時点で再バインドされてます。
何日かすこしずつコードを書きながら勉強して行くことで、WPFのBindingについておぼろげながら、掴めてきました。
まだまだ奥深いBindingですが、ここからは実際に何かしらのアプリケーションを作りつつ理解を深めていった方がいい気もします。 さて、何を作ろうか… どうせ作るならMVVMで作っていきたいですなー