WPF Bind Modeの動きを確認
BindingクラスのBindingModeについて、改めて動きを確認してみました。
まずビュー側
<Window x:Class="WhatIsBindingMode.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:WhatIsBindingMode" mc:Ignorable="d" Title="MainWindow" Height="350" Width="525"> <Window.Resources> <Style TargetType="Label"> <Setter Property="Height" Value="24"/> <Setter Property="HorizontalAlignment" Value="Right"/> </Style> <Style TargetType="TextBox"> <Setter Property="Height" Value="24"/> </Style> </Window.Resources> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="Auto" /> <ColumnDefinition Width="*"/> </Grid.ColumnDefinitions> <StackPanel Grid.Column="0"> <Label Content="Mode = Default:"/> <Label Content="Mode = OneTime:"/> <Label Content="Mode = OneWay:"/> <Label Content="Mode = OneWayToSource:"/> <Label Content="Mode = TwoWay:"/> </StackPanel> <StackPanel Grid.Column="1"> <TextBox Text="{Binding Path=ModeDefault ,Mode=Default}"/> <TextBox Text="{Binding Path=ModeOneTime ,Mode=OneTime}"/> <TextBox Text="{Binding Path=ModeOneWay ,Mode=OneWay}"/> <TextBox Text="{Binding Path=ModeOneWayToSource ,Mode=OneWayToSource}"/> <TextBox Text="{Binding Path=ModeTwoWay ,Mode=TwoWay}"/> </StackPanel> </Grid> </Window>
このViewの実行画像が以下の通り、各テキストボックスに異なるModeプロパティを設定しています。
続いてViewModel側、Modeプロパティの値と対応するプロパティを持っています。
呼び出しのタイミングを確認するため、プロパティ内のステップの先頭とプロパティの値が変更された場合にメッセージ出力をします。
using System.Diagnostics; using System.ComponentModel; namespace WhatIsBindingMode { public sealed class ViewModel : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; private void RaisePropertyChanged(string propertyName) { if (this.PropertyChanged == null) return; this.PropertyChanged( this, new PropertyChangedEventArgs(propertyName) ); var message = string.Format("Property {0} Changed", propertyName); Debug.WriteLine(message); } private string _modeDefault = string.Empty; public string ModeDefault { get { Debug.WriteLine("ModeDefault.get Called"); return this._modeDefault; } set { Debug.WriteLine("ModeDefault.set Called"); if (this._modeDefault == value) return; this._modeDefault = value; this.RaisePropertyChanged(nameof(this.ModeDefault)); } } private string _modeOneTime = string.Empty; public string ModeOneTime { get { Debug.WriteLine("ModeOneTime.get Called"); return this._modeOneTime; } set { Debug.WriteLine("ModeOneTime.set Called"); if (this._modeOneTime == value) return; this._modeOneTime = value; this.RaisePropertyChanged(nameof(this.ModeOneTime)); } } private string _modeOneWay = string.Empty; public string ModeOneWay { get { Debug.WriteLine("ModeOneWay.get Called"); return this._modeOneWay; } set { Debug.WriteLine("ModeOneWay.set Called"); if (this._modeOneWay == value) return; this._modeOneWay = value; this.RaisePropertyChanged(nameof(this.ModeOneWay)); } } private string _modeOneWayToSource = string.Empty; public string ModeOneWayToSource { get { Debug.WriteLine("ModeOneWayToSource.get Called"); return this._modeOneWayToSource; } set { Debug.WriteLine("ModeOneWayToSource.set Called"); if (this._modeOneWayToSource == value) return; this._modeOneWayToSource = value; this.RaisePropertyChanged(nameof(this.ModeOneWayToSource)); } } private string _modeTwoWay = string.Empty; public string ModeTwoWay { get { Debug.WriteLine("ModeTwoWay.get Called"); return this._modeTwoWay; } set { Debug.WriteLine("ModeTwoWay.set Called"); if (this._modeTwoWay == value) return; this._modeTwoWay = value; this.RaisePropertyChanged(nameof(this.ModeTwoWay)); } } } }
このViewModelをMainWindowのコードビハインドでDataContextプロパティに渡しています。
using System.Windows; namespace WhatIsBindingMode { /// <summary> /// MainWindow.xaml の相互作用ロジック /// </summary> public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); this.DataContext = new ViewModel(); } } }
これで準備完了。
画面起動時
まずは画面を起動したときのメッセージ。
ModeDefault.get Called ModeOneTime.get Called ModeOneWay.get Called ModeOneWayToSource.set Called ModeTwoWay.get Called
OneWayToSource以外はViewModel側のgetterが呼び出され、OneWayToSourceのみsetterが呼び出される。
OneWayToSourceはView⇒ViewModelの一方向のバインドなので、ViewModelの内容を問わずViewの値を更新するわけね。
DefaultとTwoWay
DefaultでバインドしているTextBoxに「hoge」と入力してタブ移動
ModeDefault.set Called Property ModeDefault Changed ModeDefault.get Called
TextBoxの既定値はTwoWayなのでTwoWayでも同じ
ModeTwoWay.set Called Property ModeTwoWay Changed ModeTwoWay.get Called
ここで少しひっかかる、PropertyChangedイベンドが発生した後にProperty ~ Changedってメッセージを出力しているのに、
~.get Calledのメッセージが出力されるのはそれよりさらに後、
イベントが発生したタイミングでGetterから値を取得するなら~.get Calledが先に出力されるのでは???
うーん・・・
OneTime
OneTimeは一度だけViewModelから値を取得するので画面で操作しても反応なし。
OneWay
OneWayはViewModelからViewへの一方的な連動なので画面で操作しても反応なし。
OneWayToSource
OneWayToSourceはOneWayの逆方向でViewからViewModelへの一方的な連動なので、
Setterのみ呼ばれ、Getterは呼ばれない。
ModeOneWayToSource.set Called Property ModeOneWayToSource Changed
ViewModelの更新
面倒になったのでソースは出さないけど、
ViewModelのSetterで直接値を更新すると、
Default、TwoWay、OneWayが指定されているプロパティがViewに反映され、
OneTime、OneWayToSourceは反応なしになるわけですね。