Windows App SDKとC++を使用したアプリで、CheckBoxのIsCheckedプロパティをTwoWayモードでバインドします。ただそれだけです。 IsCheckedのプロパティをバインドすることで、ドメインロジックの層ではCheckBoxを意識することなく、ただバインドされたbool型の変数を参照すればよくなります。
前置き
twowayモードを使用しますが、データの流れとしてはGUIのチェックボックスの状態をある変数の値に反映させる方向のみであり、例えばプログラムから画面のチェックボックスのチェック状態を操作するようなことは行いません。 この理由としては単にWindows App SDKのBinding Modeに画面から変数(ターゲットからソース)の向きのみを扱うモードが無いからです。
BindingMode 列挙型を見れば分かるように、OneTime
/OneWay
/TwoWay
しかモードがありません。なのでCheckBoxのようにユーザが操作できるGUIの状態を変数にバインドするには必然的にTwoWayモードを使用することになります。
余談ですが.NETのAPIにはOneWayToSource
というターゲットからソースへの一方向のバインドを行うモードがあります。
アプリの動作
今回作成したアプリの挙動を説明します。
画面にはボタン、テキストブロック、チェックボックスの3つの要素があります。 ボタンをクリックすると、テキストの内容がチェックの有無に応じた値に更新されます。 チェックが入っていれば"Checked!"、チェックがなければ"Unchecked..."と表示されます。
クラス構成
一応ビューとビューモデルを分離しています。チェックボックスのIsCheckedプロパティとテキストブロックのTextプロパティはCheckBoxViewModelのメンバ変数にバインドされているので、ビュークラスの仕事はクリックイベントの処理のみです。
コード内容
プロジェクトはGithubにあげたのですべて見たい場合はそちらを確認していただくことにして、要点のみを説明します。
まずはxamlでGUIの要素を作成します。
TextBlockはバインドした変数の内容を表示するだけなのでModeがOneWay
になっているのに対し、CheckBoxのIsChecked要素はTwoWay
としています。
<StackPanel Orientation="Vertical" HorizontalAlignment="Center" VerticalAlignment="Center"> <Button x:Name="myButton" Click="myButton_Click">Click Me</Button> <TextBlock Text="{x:Bind ViewModel.Text, Mode=OneWay}"></TextBlock> <CheckBox Content="Do you agree?" IsChecked="{x:Bind ViewModel.IsChecked, Mode=TwoWay}"/> </StackPanel>
次にCheckBoxViewModelの定義です。
idlファイルではXamlからアクセスできるようにプロパティのゲッター、セッターを宣言します。
idlファイルにいちいち書かないといけないのがC#で書くときに比べて冗長に感じるところです。。。
テキストを更新したタイミングでGUIに通知を送信するためにMicrosoft.UI.Xaml.Data.INotifyPropertyChanged
を継承しています。
namespace TwoWayViewModel { [Microsoft.UI.Xaml.Data.Bindable] runtimeclass CheckBoxViewModel : Microsoft.UI.Xaml.Data.INotifyPropertyChanged { CheckBoxViewModel(); Int32 MyProperty; Boolean IsChecked; String Text{ get; }; void UpdateText(); } }
個人的に詰まったポイントがBoolean
の表記です。
MIDL Predefined and Base Typesを確認し、boolean
表記にしていたのですが、そうすると以下のエラーが発生します。
Classic WinRT IDL constructs cannot be used in Modern WinRT IDL types [context]: boolean
クラシックなWinRTとモダンなWinRTの違いがわからないので正確なことは言えないですが、Boolean
表記とすることでビルドが通りました。
IsCheckedの実装は以下の通り、単にbool型の変数のゲッター、セッターです。
boolean IsChecked() { return is_checked_; }; void IsChecked(boolean val) { is_checked_ = val; }; boolean is_checked_ = true; // デフォルト値
これでis_checked
変数にチェックボックスの値がバインドされます。
ソースからターゲットへのOneWayのバインドの場合は、値を更新したら通知イベントを発行する必要がありますが、ターゲットからソースへの場合はターゲット側の値が変化すれば自動で変数に反映されます。
あとはCheckBoxViewModelで処理を行う際にはチェックボックスを意識せずにis_checked
を参照すればよいです。簡単ですね。