はじめに
こんにちは、WPF歴2週間の新米エンジニアです!今回はこの2週間の学習の中で一番大切な概念だな~と思ったデータバインディングについての備忘録を残しておきたいと思います。
データバインディングとは
アプリケーションのUI要素とビジネスロジック(そのプログラムで行いたい処理が実装されている箇所)を関連付けるプロセスのことです。
バインディングを適切に設定し、かつデータが通知を提供する場合、データの値が変更されたとき、UIに表示されている要素も更新されます。
逆にUIの要素を変更すると、もとになるデータが自動的に更新されるようにすることもできます。
例えばテキストボックスの内容を書き換えるとそれと紐づけられているデータも書き換えられます。
バインディングにはコントロール同士のバインディングやコントロールとデータモデルとのバインディングがあります。以下にそれぞれについて説明します。
コントロール同士のバインディング
例えばテキストボックスに入力した値をテキストブロックに反映させることが挙げられます。こちらは簡単です。
xamlに以下のように記述すれば実現できます。テキストボックスAとテキストブロックAをバインディングする例です。
<TextBox x:Name="TextBoxA" Text="{Binding ElementName=TextBlockA, Path=Text, Mode=TwoWay, UpdateSourceTrigger=Explicit}"/>
<TextBlock x:Name="TextBlockA"/>
- ElementName:バインド先のコントロール名を指定する
- Path:バインド先のプロパティ名を指定する
- Mode:プロパティの変更通知の方向を設定できる(後節で説明)
- UpdateSourceTrigger:バインド先へのプロパティ変更のタイミングを指定できる(後節で説明)
ここで注意点ですがバインディングされるコントロールには必ずNameを付ける必要があります。
当然ですがそれがないとどの要素をバインディングするかわかりませんもんね。
データバインディングを実現するには「データが通知を提供する」必要がありますが私たちで書く必要はありません。
なぜならコントロールのプロパティはほとんどが依存関係プロパティといってプロパティの値の変更通知を自動的に行う仕組みを持っているからです。
コントロールとモデルのバインディング
こちらを実現するにはバインドしたいプロパティを持つクラスがINotifyPropertyChangedを継承することで実現することができます。
その上でバインディングしたいオブジェクトをコントロールのDataContextに設定します。
- DataContext
UI要素(FrameworkElementを継承したオブジェクト)が持つ、コントロールに対応するデータが色々入ってるオブジェクト型のプロパティ。
ここではあらかじめ別で定義しているfoodクラスを使用しています。
public MainWindow()
{
InitializeComponent();
food Name ="Takoyaki"
//モデルを作成しDataContextに設定
this.DataContext = food;
}
<Window x:Class="WPF.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="450" Width="800">
<StackPanel>
<!--モデルとDataContextに設定されているFoodのプロパティとバインドされる-->
<TextBlock Text="{Binding Path=Name}"/>
<TextBlock Text="{Binding Path=Price}"/>
</StackPanel>
DataContextは親VisualのDataContextを参照します。ここではMainWindowクラスのインスタンスが親VisualですのでそのDataContext、foodが参照されることになります。
Modeプロパティ
BindingクラスのModeプロパティはプロパティ変更通知の方向を設定するものです。以下の4種類が存在します。
- OneWay ソースの変更が一方向にUI要素に反映される
- OneTime ソースの変更が一方向にアプリケーション起動時またはDataContextが切り替わったとき一度だけUI要素に反映される
- TwoWay ソース、UI要素の変更が双方向に反映される
- OneWaytoSource UI要素の変更が一方向にソースへ反映される
UpdateSourceTriggerプロパティ
BindingクラスのUpdateSourceプロパティはバインド先へのプロパティ変更のタイミングを指定するものです。
- LostFocus バインディングされている要素からフォーカスが外れたタイミング。
- PropertyChanged バインディングされている要素のプロパティが変更されたタイミング。
- Explit 明示的に指定したタイミング。上二つのモードでは勝手にプロパティの変更がされてしまいますがExplitを指定し、別でタイミングを定義することで好きなタイミングでプロパティの変更がなされるようにできます。
Converter
異なる型のプロパティをバインディングするために値を型変換するものです。
IValueConverterを継承しConvert、ConvertBackの二つのメソッドを実装することで順方向、逆方向それぞれに対応した型変換を行えるようになります。
ここでは実験としてチェックボックスをチェックすれば「true」外せば「false」の文字列がテキストボックスに入力されるように、またテキストボックスに入力された文字列の長さが偶数ならチェックが入れられ、奇数ならチェックが外されるという実装をしています。
public class Converter : IValueConverter
{
//テキストボックスに入力された文字数が偶数の時、CheckBoxをチェックします。
//奇数の時、チェックを外します。
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
//Value にはTextBox のTextが入っている(object型)
//CheckBoxのチェックにコンバート
var objectValue = value as string;
if (objectValue.Length %2==0)
{
return true;
}
else
{
return false;
}
}
// CheckBoxのチェックが入っている時、"true"を返します。
// CheckBoxのチェックが入っていない時、"false"を返します。
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
//Value にはCheckBoxのチェックが入っている(object型)
var objectValue = value as bool?;
if (objectValue == true)
{
return "true";
}
else
{
return "false";
}
}
}
converterを用意した後はxamlの方で使用できるようにします。
<!--コンバーターを使う準備 -->
<Window.Resources>
<local:Converter x:Key="ConverterA"/>
</Window.Resources>
<!--コンバーターを通してバインディング テキストとチェックが双方向に チェックが変更された時にConvertBack テキストが変更された時Convertが動く -->
<CheckBox x:Name="`CheckBoxA" IsChecked="{Binding ElementName=TextBoxA,Path=Text,Mode=TwoWay,Converter={StaticResource ConverterA}}"></CheckBox>
<TextBox x:Name="TextBoxA"></TextBox>
これで望んだ処理が行えるようになります。
以上です。これからもWPFの学習を進めていきたいと思います!頑張っていきましょー!