WPF Prism episode: 9′ ~ ReactiveProperty の Validation は DataAnnotation じゃないと思った? Ⅱ ~

← 前のページ【WPF Prism episode: 9 ~ ReactiveProperty の Validation は DataAnnotation じゃないと思った?~】

今回の WPF episode シリーズ第 9 弾はとある事情(とある事情は前のページに書いています)により 2 回のエントリに分けて公開していて、このページは前のページからの続きになります。
検索エンジン等から直接このページに来られた場合は前のページから読んでもらえるとありがたいです。

前のページは Extended WPF Toolkit™ を紹介した所で終わりましたが、元々このエントリは ReactiveProperty で定義したバインドプロパティへ Validation を適用する方法を紹介するのが本題なので話を戻します。

複数のプロパティ項目を参照する Validation

後回しにしていた測定日の Validation ですが、測定日に使用している DateTimePicker コントロールは、TextBox と動作が多少違います。

まず、DateTimePicker の Validation は前のページで紹介した UpdateSourceTrigger = PropertyChanged を設定しても、しなくても UpdateSourceTrigger = LostFocus と同じ動作になります
そのため、RegularExpression 属性を設定しても Validation が実行されるのはフォーカスが他のコントロールへ移った後なので、設定する意味が大してありません。
これは WPF 標準の DatePicker も同じ動作なので、DataPicker 系には入力文字種チェック等を設定しなくてもコントロール側である程度よろしくやってくれると考えていいと思います。

そして、測定日の項目はサンプルアプリ上で表示上のキー項目として扱いたいため、『リスト内に存在する同一日付の入力は不可とする』ようなモデルの他項目も関係する Validation を設定する方法を紹介します。

結論から言うと、View とバインドするプロパティを ReactiveProperty で定義していて、複数項目が関係する Validation を行う場合、DataAnnotation は使用できません
SourceChord の『WPFでの入力値検証・その7 ~独自の検証ロジックを作成する~』『WPFでの入力値検証・その8 ~複数のプロパティが絡むバリデーションを作成する~』 に書かれている方法で Validation を実行しようとしても、ReactiveProperty で定義したプロパティでは希望通りに動作しません。

理由として、上記 SourceChord のエントリに書かれている【ValidationAttribute 派生クラス】、【CustomValidation 属性】の両方とも Validation で呼び出されるメソッド内から VM が参照できないのが最も大きな理由です。

CustomValidation 属性を使用する場合の属性コンストラクタは以下のように指定します。

CustomValidation(typeof(VM クラス名), “Validationメソッド名”

CustomValidation コンストラクタの第 1 パラメータは Validation メソッドを定義したクラスの type 、第 2 パラメータには Validation メソッド名を指定します。

又、CustomValidation に指定したメソッドは以下のようなシグネチャで作成します。

public static ValidationResult【Method Name】(【type】value, ValidationContext context)

作成した Validation メソッド内にブレークポイントを設定して確認すると、第 2 パラメータ: ValidationContext.ObjectInstance には null が渡されます。
又、CustomValidation に指定するメソッドは static で定義する必要があるので、どんなデータも渡せるとは限りません。
そのため、CustomValidation は複数項目が関係する Validation に使用するのは難しいと考えた方が良いでしょう。

ValidationAttribute 派生クラスを作成する場合も同様に override する IsValid メソッドの第 2 パラメータ: ValidationContext.ObjectInstance は ReactiveProperty に設定している型(ここでは Nullable<DateTime>)が渡ってくるため同じく VM が取得できません。

ReactiveProperty で定義していないプロパティであれば、ValidationContext.ObjectInstance プロパティから VM が取得できる(管理人は未確認)ようですが、ReactiveProperty で定義したプロパティの場合、ValidationAttribute 派生クラスと CustomValidation 属性のどちらを使用しても VM のインスタンスを取得出来ないので、他項目が関係する Validation には使用出来ないと言えます。

間違えないでほしいのが、ValidationAttribute 派生クラスと CustomValidation 属性 はReactiveProperty に設定は出来る と言う点です。
設定は可能で項目単体の Validation であればどちらの場合も問題なく設定できるが、他項目が関係する Validation には使用できない
と、言う意味なので間違えないでください。

とは言え、上にも書いたように項目単体の Validation であれば RegularExpression 属性で大半がカバーできるため、ValidationAttribute 派生クラスや CustomValidation 属性を使用する意味があるのかと言われると、管理人的には無いと思っています。

本来、DataAnnotation を使用して Validation を行う場合は、VM へ INotifyDataErrorInfo インタフェースを実装する必要がありますが、ReactiveProperty の場合は ReactiveProperty自身が INotifyDataErrorInfo インタフェースを実装している為、このような動作になっていると考えられます。
これは ReactiveProperty 構造的な仕様なのでどうにもなりませんが、他項目が関係する Validation が出来ない訳ではありません。

ReactiveProperty.SetValidateNotifyError で複数項目の Validation を実行する

では他項目が関係する Validation を ReactiveProperty で定義したプロパティへ設定するにはどうするかと言うと、以下で紹介する【SetValidateNotifyError】拡張メソッドを使用します
そして、ReactiveProperty は DataAnnotation と SetValidateNotifyError 拡張メソッドを同時に設定できないため、他項目が関係する Validation を設定する場合は、SetValidateNotifyError 拡張メソッドのみを指定することになります。

SetValidateNotifyError() 拡張メソッドは以下のように記述します。

この Validation には身体測定データリスト自体が必要なので、あらかじめデータオブジェクトのインスタンスを Unity からコンストラクタへインジェクションしてもらいます。(68行目)

そして、測定日 ReactiveProperty の初期化時に SetValidateNotifyError() メソッドを呼び出してデリゲート先を設定しています。
ここはデリゲートではなくラムダ式で直接記述することもできますが、管理人的には記述する行数が多い場合はラムダ式で書くと煩雑に感じるので、別メソッドへ切り出すのが管理人の好みです。

SetValidateNotifyError() メソッド内で null 又は string.Empty を返すと ReactiveProperty でエラー無しと判定されるので、正常値の場合は null を返しています。

又、Validation メソッド内部ではデータオブジェクトに追加した重複チェックのメソッドを呼び出していて、以下のような重複をチェックするメソッドを呼び出しています。

ここまでの状態で実行して『新しい測定』ノードを選択して、編集 View を表示します。

fig.1 初回表示

エラーを表す赤枠が、測定日へいきなり表示されています。
Validation メソッドの中で『null はエラー』としているので当然ですが、初めて開く画面でいきなりエラーが表示されるのは、UX 的に問題です。

このような初期値エラーを無視するには ReactiveProperty の初期化時に指定している ToReactivePropertyAsSynchronized 拡張メソッドの省略可能第 2 パラメータ:mode へ以下のように初回 Validation のエラーは無視する設定を追加します。

上記のように第 2 パラメータの mode をセットして実行すると、初期値エラーの赤枠が表示されなくなります。
尚、身長・体重の両プロパティは初期値としてエラーにならない値をセットしているので、測定日プロパティのように第 2 パラメータをセットしなくても、初期表示時にエラーは表示されません。

これで、身体測定データの全項目へ Validation を設定しましたが、 DataAnnotation 等で指定しているエラーメッセージが View へ表示されていません。
これは WPF 標準のエラーテンプレートがエラーメッセージを表示するようになっていないのが理由なので、エラーメッセージが表示されるように XAML を編集する必要がありますが、これ以上書くと又、WordPress のプラグインの問題で全文が表示されないので、今回はここで打ち切りとします。

 

 

前ページにも書きましたが、本来は 1 回分のエントリとして公開する予定だったのを 2 回に分けて公開しましたが、それでも収まり切りませんでした。
又、前回のエントリで Prism の IConfirmNavigationRequest インタフェースを紹介すると宣言したにも拘らず、結局次回へ持ち越すことになってしまいました。

そして次回こそは、今回の残り Validation の ErrorTemplate と Prism の IConfirmNavigationRequest インタフェースの紹介をする予定に加えて Prism 組み込みのメッセージボックスを表示する方法の紹介を予定しています。

尚、いつもの通り今回のソースコードも GitHub リポジトリ にアップしています。

 

【WPF Prism episode: 10 ~ ErrorTemplate は Resources タグ、時々、ResourceDictionary ファイルのなか。~】次回記事 →

あわせて読みたい

コメントを残す

メールアドレスが公開されることはありません。

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください

%d人のブロガーが「いいね」をつけました。