例えば検索画面で、検索文字列を1文字入力するごとに検索結果の表示を絞り込んでいきたい場合など、テキストボックスに入力中の文字列をバインド元に反映させて処理する方法を説明する。
powered by Insider.NET
テキストボックスに入力中の文字列をバインド元に反映させて処理をさせたいことはないだろうか? 例えば、検索する画面で、検索文字列を1文字入力するごとに検索結果の表示を絞り込んでいきたい場合などだ。Windows 8(以降、Win 8)のWindowsストアアプリでは、それはバインディングでは実現できなかった。本稿では、Win 8でそれを実現していた方法を振り返り、Windows 8.1(以降、Win 8.1)での新しい方法を2通り紹介する。なお、本稿のサンプルは「Windows Store app samples:MetroTips #65(Windows 8.1版)」からダウンロードできる。
Win 8.1用のWindowsストアアプリを開発するには、Win 8.1とVisual Studio 2013(以降、VS 2013)が必要である。本稿ではOracle VM VirtualBox上で64bit版Windows 8.1 Pro(日本語版)とVisual Studio Express 2013 for Windows(日本語版)*1を使用している。
*1 マイクロソフト公式ダウンロードセンターの「Microsoft Visual Studio Express 2013 for Windows」から無償で入手できる。
これから行う実験のベースとなるプログラムを作っておく。本稿ではXAMLコードの詳しい説明は省略させていただくので、次の画像を参考にして適宜UIを構築してほしい。
ベースとなるプログラムでは、バインディングソースとなるstringオブジェクトを用意し、TextBoxコントロール(Windows.UI.Xaml.Controls名前空間)をそれにバインドする(次の図)。
実装は以下のように進める。まずWindowsストアアプリのプロジェクトを作るときに、[新しいアプリケーション (XAML)]を選ぶ。出来上がったプロジェクトから「MainPage.xaml」ファイルを削除し、あらためて[基本ページ]を「MainPage.xaml」ファイルとして追加する。これにより、プロジェクトにCommonフォルダーとその中にソースコードが幾つか自動生成される。これは、以降でデータバインディングの実装を簡単に行うために必要だ。
次に、「MainPage.xaml」ファイルにTextBoxコントロールを2つ配置し、データバインディングを設定する(次のコード)。
<TextBox Header="入力" ……省略……
Text="{Binding Path=SampleText1, Mode=TwoWay}" />
<TextBox Header="バインド" ……省略……
Text="{Binding Path=SampleText1}" />
最後に、コードビハインドでバインディングソースの初期値を設定しておく(次のコード)。
private void navigationHelper_LoadState(object sender, LoadStateEventArgs e)
{
this.DefaultViewModel["SampleText1"] = "TEST1";
}
Private Sub NavigationHelper_LoadState(sender As Object, e As Common.LoadStateEventArgs)
Me.DefaultViewModel("SampleText1") = "TEST1"
End Sub
これで実行してみると、[入力]欄に入力している最中には[バインド]欄に反映されないことが確認できる。TextBoxコントロールとのデータバインドでは、1文字入力するごとにはバインディングソースへ反映されないのだ。[入力]欄からフォーカスを移動したときだけ反映されるのである。
Win 8では、1文字入力するごとに処理するには、以下のようにしてTextChangedイベントでロジックを呼び出すしかなかった。
データバインディングはあきらめて、入力用のTextBoxコントロールにはTextChangedイベントを追加し、出力用のTextBoxコントロールには名前を付ける(次のコード)。
<TextBox TextChanged="inputText1_TextChanged" Header="入力" ……省略…… />
<TextBox x:Name="outputText1" Header="イベント" ……省略…… />
次に、イベントハンドラーでロジックを呼び出して、結果を出力用のTextBoxコントロールに書き出すようにする(次のコード)。
private void inputText1_TextChanged(object sender, TextChangedEventArgs e)
{
var input = (sender as TextBox).Text;
// 本来はここでinputを使ってロジックを呼び出す
this.outputText1.Text = input;
}
Private Sub inputText1_TextChanged(sender As Object, e As TextChangedEventArgs)
Dim input = CType(sender, TextBox).Text
' 本来はここでinputを使ってロジックを呼び出す
Me.outputText1.Text = input
End Sub
これで実行してみると、確かに1文字入力するごとに反映される。
しかし、実際に開発する場面を考えてみると、他のところではデータバインディングで画面とロジックをつないでいるのに、テキストボックスだけはコードビハインドからロジックを呼び出すというのでは、画面とロジックのインターフェースがバラバラである。以降で説明するように、Win 8.1では、この問題は簡単に解決できるようになった。
Win 8.1では、新設されたBindingExpressionクラス(Windows.UI.Xaml.Data名前空間)を使うことで、コントロールの内容を強制的にバインディングソースへ反映できるようになった。
まず、最初のXAMLコードに対して、データバインディングを残したままで、入力用のTextBoxコントロールにTextChangedイベントを追加する(次のコード)。
<TextBox TextChanged="inputText1_TextChanged" Header="入力" ……省略……
Text="{Binding Path=SampleText1, Mode=TwoWay}" />
<TextBox Header="バインド" ……省略……
Text="{Binding Path=SampleText1}" />
そして、[入力]用のTextBoxコントロールの内容をイベントハンドラーで強制的にバインディングソースへ反映させるようにするのだ(次のコード)。
private void inputText1_TextChanged(object sender, TextChangedEventArgs e)
{
// BindingExpressionを使って強制的にバインディングソースへ反映させる
var be = (sender as TextBox).GetBindingExpression(TextBox.TextProperty);
be.UpdateSource();
}
Private Sub inputText1_TextChanged(sender As Object, e As TextChangedEventArgs)
' BindingExpressionを使って強制的にバインディングソースへ反映させる
Dim be = CType(sender, TextBox).GetBindingExpression(TextBox.TextProperty)
be.UpdateSource()
End Sub
これでTextBoxコントロールに1文字入力するごとに、その内容がバインディングソース(を介してバインディング先のコントロール)に反映されるようになるので、確かめてほしい。
この方法で画面とロジックのインターフェースをデータバインディングに統一できるのだが、実際には次に説明する方法の方が簡単だ。このBindingExpressionを使って強制的にバインディングソースへ反映させる方法は、むしろユーザーアクション(ボタンのタップなど)に基づいてバインディングソースへ反映させたいときに有効である。
さらに、Win 8.1でBindingクラス(Windows.UI.Xaml.Data名前空間)に追加されたUpdateSourceTriggerプロパティを使えば、イベントに頼らずに済むようになる。このプロパティに「PropertyChanged」と設定するだけである(次のコード)。
<TextBox Header="入力" ……省略……
Text="{Binding Path=SampleText1, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<TextBox Header="バインド" ……省略……
Text="{Binding Path=SampleText1}" />
なお、UpdateSourceTriggerプロパティの既定値は「Default」であり、その動作はMSDNには「PropertyChangedの値と同じ値として評価されます」と記述されているが、TextBoxコントロールにおいては誤りである。TextBoxコントロールのバインディングで「Default」を指定すると、WPFで「LostFocus」を指定したときと同じ動作(=フォーカスを失ったときに反映)になる(ただし、WindowsストアアプリのUpdateSourceTriggerプロパティには「LostFocus」を指定できない)。
Win 8.1でテキストボックスに入力中の文字列をバインディングソースに反映させるには、バインディングの指定に「UpdateSourceTrigger=PropertyChanged」を追加すればよい。また、ユーザーアクションのタイミングでバインディングソースへ反映させるには、BindingExpressionクラスを利用する。
BindingExpressionクラスについては、次のドキュメントも参照してほしい。
UpdateSourceTriggerプロパティについては、次のドキュメントも参照してほしい。
Copyright© Digital Advantage Corp. All Rights Reserved.