初心者が迷ったReactivePropertyを使用した数値型のView Model
Modelが出来たら、いよいよView Modelです。
さて、ここまで書いて、WPF+MVVM+ReactiveProperty+Rx+LINQがどれも始めて半年程度の自分が、LINQ式の中にObservableなメタデータを埋め込んでいくところで罠にはまりまくったことが段々思い出されてきました・・・
っていうか、記事のサンプルコードを書きながらリファクタリングしていく内に、同じ罠にハマったから、思い出したんですけどね。
- 一番有効桁数が多く丸め処理が細かく出来るdecimalで検証後に必要な精度に丸めてキャストする
- いろんな型の拡張メソッドを作るより分かりやすい
これは、撤回します。
ハマりどころがあるので、必要な分だけハマらないように実装した拡張メソッドを作って使いまわした方が良い・・・
View Modelの実装方針
ちょっと、方針を整理します。
- 書式指定や国際化など全部の載せのObservableじゃない拡張メソッドを作る
- 自分が使いそうな分だけオーバーロードして簡素化したObservableじゃない拡張メソッドを作る
- 取りあえずカレントカルチャ (まあ、コントロールでカルチャ変えるなんてレポート用コントロールくらいなんで・・・)
- 表示は3桁区切り付き
- 入力は3桁区切りは有っても無くても良い (ちなみに3桁おきじゃなくてもそれなりに処理してくれる・・・)
- 入力は小数点は有っても無くても良い
- 小数点以下の桁数は後ろにつけた連続した0を除いた桁数が指定桁数以上だとNG (整数で3.00はOKだが3.10はNG)
- 丸めは四捨五入 (今回、桁オーバーは弾いているので意味ないが・・・)
- Observableに変えていく
- LINQを書きやすいようにリファクタリング
拡張メソッド
まずは、全部載せのObservableじゃない拡張メソッド。
次に、自分が使いそうな分だけオーバーロードして簡素化したObservableじゃない拡張メソッドを作る。
テスト画面
ここまで、やってこんな感じで画面を作る。
まず、メタデータを画面で変更できるようにします。
本来は、データソースから取得して、データ編集画面では編集しないことの方が多いですが、変更が数値型の表示に直ぐに反映されることを確認するために、敢えて編集項目とします。
本当は、コントロールの外に出せば良いのですが、サボっています。
UpperBoundは、CombinneLatest()で即座にmodel.Decimalsの変化に同期しています。
Subscribe()でやってる怪しい処理は、
- Modelの数値型の値が変化した場合は、新しい値の再フォーマットされた文字列がView Modelに設定される
- Modelの数値型の値が変化しない場合は、View Modelに設定された入力文字列が再フォーマットされた文字列に置き変わらない
となるため、再フォーマットした文字列でView Modelを上書きするための処理です。
もう少し上手く書けないかReactivePropertyのソースコードも見て考えましたが、ダメでした。
なお、Observableじゃない拡張メソッドに渡しているReactivePropertyは、ラムダ式の中にある限り、最新の値が使用されます。
「ラムダ式の中にある限り・・・」
意外にこれが曲者です。しかし、その話はまた後で・・・
View Model
さて、桁数と上限値に依存するX、Y, Zは、複数のプロパティとCombinneLatest()で同期するように実装するのは複雑すぎます。
通常は、Modelにメタデータをセット後にデータをセットすると良いのですが、その時通知が必ず飛ぶ訳ではないので、一旦、通知をブロックして、Modelをセット後に一斉に通知が走ると、メタデータも確実に表示に反映します。
CanUpdateBindingをfalseにして、メタデータを編集後、エラーがなければ、UpdateMetaDataCommand発火後に、X, Y, Zの表示に反映されます。
画面でやるとは思ってなかったけど、これってハードウェアパラメータ設定画面で、型番をComboBoxで選択して、表示やチェック範囲を差し替える時に使えるなぁ・・・
Viewは、こんな感じになります。
だらだら、書いただけだったな・・・
次回、これを書き変える時のハマりどころを書きだしてみます。