この記事ではBindingクラスのModeプロパティの動作について確認しています。
Modeプロパティではバインディングソースとバインディングターゲット間のデータフローの方向を指定します。今回確認したのはModeプロパティに指定できる以下の2つの値についてです。
- OneWay バインディングによってソース プロパティが変更されたことにより、ターゲット プロパティは自動的に更新されますが、ターゲット プロパティへの変更は、ソース プロパティには反映されません。
- TwoWay バインディングにより、ソース プロパティまたはターゲット プロパティのいずれかが変更されると、もう一方も自動的に更新されます。
XAMLファイルは以下のとおりです。
<Window x:Class="WpfDataContextSample.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfDataContextSample"
mc:Ignorable="d"
Title="データバインディングのモードの確認" Height="500" Width="360">
<Window.Resources>
<!-- PersonCollectionクラスのインスタンス生成 -->
<local:PersonCollection x:Key="objPersonCollection" />
</Window.Resources>
<Grid DataContext="{Binding Source={StaticResource objPersonCollection}, Path=People}">
<Grid.RowDefinitions>
<RowDefinition Height="180"/>
<RowDefinition Height="80"/>
<RowDefinition Height="180"/>
</Grid.RowDefinitions>
<DataGrid Grid.Row="0"
ItemsSource="{Binding}"
AutoGenerateColumns="False"
CanUserSortColumns="False"
CanUserAddRows="False"
SelectionMode="Single"
SelectionUnit="Cell"
>
<DataGrid.Columns>
<DataGridTextColumn Header="氏名" Binding="{Binding FullName, UpdateSourceTrigger=PropertyChanged}" Width="*" CanUserSort="False" />
<DataGridTemplateColumn Header="誕生日" Width="120" CanUserSort="False">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<DatePicker SelectedDate="{Binding BirthDay, StringFormat=yyyy/MM/dd, UpdateSourceTrigger=PropertyChanged}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
<Button x:Name="BtnAddPerson" Grid.Row="1" Width="180" Height="40" Content="名無しの権兵衛を追加する" Click="BtnAddPerson_Click" />
<ListBox Grid.Row="2" ItemsSource="{Binding}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding FullName}" />
<TextBlock Text="さんの誕生日は"/>
<TextBlock Text="{Binding BirthDay, StringFormat=yyyy年M月d日}" />
<TextBlock Text="です。" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</Window>
コードビハインドは以下のとおりです。
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
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 WpfDataContextSample
{
public class Person
{
public string FullName { get; set; }
public DateTime? BirthDay { get; set; }
// コンストラクタ
public Person(string fullName, DateTime birthDay)
{
FullName = fullName;
BirthDay = birthDay;
}
}
public class PersonCollection
{
private ObservableCollection<Person> _people = new ObservableCollection<Person>();
public ObservableCollection<Person> People
{
get { return _people; }
}
public PersonCollection()
{
_people.Add(new Person("山田 太郎", new DateTime(2002, 5, 18)));
_people.Add(new Person("鈴木 花子", new DateTime(2004, 11, 6)));
_people.Add(new Person("畠山 銀次", new DateTime(1982, 7, 2)));
}
}
/// <summary>
/// MainWindow.xaml の相互作用ロジック
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void BtnAddPerson_Click(object sender, RoutedEventArgs e)
{
// リソースから PersonCollection オブジェクトを取得する
var personCollection = this.TryFindResource("objPersonCollection");
if(personCollection != null)
{
((PersonCollection)personCollection).People.Add(new Person("名無しの権兵衛", DateTime.Today));
}
}
}
}
データグリッドには氏名(DataGridTextColumn)と誕生日(DatePicker)の2列を定義し、リストボックスではDataTemplateを使用してこれらの情報を表示しています。また、データグリッドとリストボックスの間にはボタンを一つ用意しました。これは後ほど触れます。
氏名と誕生日には明示的にModeプロパティを指定していません。この場合Defaultが適用されることになります。Defaultは依存関係プロパティの種類によってOneWayやTwoWayに変わります。
One of the BindingMode values. The default is Default, which returns the default binding mode value of the target dependency property. However, the default value varies for each dependency property. In general, user-editable control properties, such as those of text boxes and check boxes, default to two-way bindings, whereas most other properties default to one-way bindings.
https://docs.microsoft.com/ja-jp/dotnet/api/system.windows.data.binding.mode?view=windowsdesktop-5.0#System_Windows_Data_Binding_Mode
氏名(DataGridTextColumn)と誕生日(DatePicker)はユーザーによって編集可能なのでTwoWayになり、TextBlockは編集不可なのでOneWayになると思われます。
プログラムの実行結果は以下のとおりです。

データグリッドの氏名と誕生日を変更するとリストボックスの項目に即時反映されます。つまりTwoWayであることが分かります。
TwoWayではバインディングターゲットであるUIを変更するとバインディングソースが更新されます。バインディングソースが更新されるため、バインディングターゲットであるリストボックス内のOneWayのTextBlockの内容が更新されます。
下図はデータグリッドの1行目の「山田 太郎」の「太郎」を消去しているところです。リストボックスの 「山田 太郎」 も連動して「太郎」が消去 されていることが分かります。

OneWayやTwoWayのデータフローの方向の詳細については以下を確認してください。

また、真ん中のボタンをクリックするとバインディングソースとして使用しているObservableCollectionオブジェクトにPersonオブジェクトが追加されます。
バインディングソースが更新されるためTwoWayのデータグリッドの2列とOneWayのリストボックス(TextBlock)に項目が追加されることがわかります。
UIは下図のように更新されます(名無しの権兵衛を追加)。

なお、ObservableCollectionを使用している理由はコレクションに項目を追加した時にUIも自動的に更新されるようにするためです。
データバインディングとUIへの通知機能の詳細については以下の記事で確認しています。
また、DataContextの振る舞いについては以下の記事で確認しています。
今回はBindingクラスのModeプロパティの動作について確認しました。
以上です。
コメント