none
MVVMパターンにおけるエラーチェックの実装方法について RRS feed

  • 質問

  • いつもお世話になります。

    複数のキー項目を画面上で入力し、一致データがあれば
    リストに出すような画面をMVVMパターンで作成したいと考えています。

    その際、項目の桁や、型のチェックを項目単位に行い、
    データの存在チェックをボタン押下時に行いたいと考えています。

    その為、存在チェックはBindingGroupでチェックしているのですが、

    上記のようにエラーチェックを行うと
    BindingGroupでエラーが発生した場合は項目単位ではなく、BindingGroup
    を設定しているエリア自体が赤くなり、項目単位のエラーと見た目が大きく
    異なるのが嫌で、回避したいと考えています。

    そこで質問なのですが、
    BindingGroupでエラーが発生した場合は、キー項目だけ赤くしたいのですが
    そのような方法はないでしょうか?

    それとも、そもそもデータ存在チェックも項目単体毎にチェックを行うものなのでしょうか?
    項目単位のチェックは入力の度に実行される為、操作性も考えるとボタン押下時のみで
    行いたいと考えているのですが、MVVMパターンではそのような実装は行わないものなのでしょうか

    2014年3月17日 6:14

回答

  • イメージですが、ボタン押下時に各項目のチェックの呼び出し+DBチェックを行うようなイメージでしょうか。

    はい。その通りです。このようにしないと、画面を開いた直後にボタンを押下した場合など、例えばデフォルトは空白でも空白ではいけない項目などのチェックを行うことができません。先に触れた、プロパティ名とエラー文をペアで持つDictionaryをチェックし、どのプロパティ値もエラー文が空であれば、初めてDBチェックを行うようにすれば良いでしょう。
    また、Dictionaryをチェックしてエラーがあるかどうかを返すプロパティを用意し、それによってボタンの有効、無効を切り替えるようにしても良いでしょう。具体的にはDataTriggerでボタンのIsEnabledを制御してやります。ただし、最初の1回目は、エラーチェックを行っていないので本当にエラーがあったとしてもボタンが押せてしまいますが、それはそれで仕様的に問題ないと思います。

    ★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/

    • 回答としてマーク VbBeginer001 2014年3月24日 3:59
    2014年3月20日 0:45
    モデレータ

すべての返信

  • 解決策ではありませんが一点だけ。

    > MVVMパターンではそのような実装は行わないものなのでしょうか

    MVVMはそもそも概念であって、実装方法を既定するものではありません。今回の質問はむしろMVVMとは関係なく、

    BindingGroupでエラーが発生した場合は、キー項目だけ赤くしたいのですが
    そのような方法はないでしょうか?

    という問題点だけだと思います。


    ひらぽん http://d.hatena.ne.jp/hilapon/


    2014年3月17日 6:56
    モデレータ
  • ひらぽんさんご指摘ありがとうございます。

    もっと良い実装方法があれば教えて頂ければと、曖昧な質問になってしまいました

    2014年3月18日 0:26
  • BindingGroupでエラーが発生した場合は、キー項目だけ赤くしたいのですが
    そのような方法はないでしょうか?

    Validation.ErrorTemplateに独自のテンプレートをセットすることになると思います。キー項目を赤枠で囲むのであれば、キー項目もそれぞれエラーチェックをするようにするのが簡単でしょう。そうなるとBindingGroupを使う意味が無くなってきますね・・・。ただ、実際のエラーチェックはBindigGroupのエラーチェックのみにし、そのエラー結果を当該の項目にセットするようにする方法もアイデアとしては浮かびます。

    ちなみに私はBindingGroupは使わずに、項目を個別にチェックしています。ただ、項目間の相互チェックとなると工夫が必要ですが、不可能なわけではありません。


    ★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/

    • 回答の候補に設定 星 睦美 2014年3月19日 1:12
    2014年3月18日 4:32
    モデレータ
  • trapemiyaさん、ご回答ありがとうございます。

    「実際のエラーチェックはBindigGroupのエラーチェックとする 」というのがあまりぴんとこないのですが、

    CommandのCanExecuteの中でBindigGroup内のチェック処理を呼び出すイメージでしょうか?

    項目間の相互チェックまでなら、各項目のチェックの中でいけそうな事はないのですが、

    テキストボックスはUpdateSourceTrigger=PropertyChangedで、入力の度にチェック処理を走らせているので

    DBとやり取りするチェックはレスポンスを考えると、入れたくないので

    なんとかBindigGroupのエラーを使用する方法を模索したいと思います。

    2014年3月18日 10:39
  • 「実際のエラーチェックはBindigGroupのエラーチェックとする 」というのがあまりぴんとこないのですが、

    CommandのCanExecuteの中でBindigGroup内のチェック処理を呼び出すイメージでしょうか?

    いえ、そうではなくて、Validateをoverrideしたメソッド内でエラーチェックを行い、エラーになった場合は、IDataErrorInfoでエラーになるようにその項目を設定するということです。
    基本的には、以下のページのイメージになりますが、各項目のエラーチェックを各項目のプロパティで行わず、Validateをoverrideしたメソッド内で行うということです。そのためには少し工夫が必要で、Dictionaryで各プロパティのエラー文字列を管理するようにし、
    IDataErrorInfoのメンバーであるpublic string this[string columnName]は、このDictionaryからエラー文字列を返すようにすると良いでしょう。

    Using BindingGroups for greater control over input validation
    http://www.scottlogic.com/blog/2008/11/28/using-bindinggroups-for-greater-control-over-input-validation.html

    項目間の相互チェックまでなら、各項目のチェックの中でいけそうな事はないのですが、

    テキストボックスはUpdateSourceTrigger=PropertyChangedで、入力の度にチェック処理を走らせているので

    DBとやり取りするチェックはレスポンスを考えると、入れたくないので

    なんとかBindigGroupのエラーを使用する方法を模索したいと思います。

    最初に、

    >データの存在チェックをボタン押下時に行いたいと考えています。

    >その為、存在チェックはBindingGroupでチェックしているのですが、

    と書かれているのですが、こことの整合性はどのようになるのでしょうか?
    また、各項目間で相互にチェックし合えば、それはDBとやり取するチェックになるわけではないのですが、どのようなことを懸念されているのでしょうか? この辺りがよくわかりませんでした。


    ★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/

    2014年3月19日 2:44
    モデレータ
  • trapemiyaさんご回答ありがとうございます。

    サイトは今目を通したばかりなので、是非参考にしたいと思います。

    すみません、私の回答内での相互チェックはDBにアクセスしない項目の相互チェック

    という認識で記載していました。(A・Bという項目があった場合、Bを入力した場合にAの項目が入力されているか、

    型が可能な型か等のチェック)

    私のやりたいと考えている内容は下記のような流れを想定しています。

    ・画面に複数の項目がある

    ・項目毎の整合性チェックは各項目入力時に行う

    ・入力終了後・ボタン押下時にDBのデータ存在チェックを行う

    ・データが無い場合は、キー項目を赤色に変更する

    日本語が不自由で申し訳ありません

    2014年3月19日 4:03
  • この場合、BindingGroupを使わない方が見通しが良くなるような気がします。
    各項目のプロパティのSetterから、その項目のエラーチェックを行うようにします。必要があれば他の項目との関連性のチェックも行います。
    エラーの結果は先に述べたようにDictionaryで管理し、IDataErrorInfoのメンバーであるpublic string this[string columnName]は、このDictionaryからエラー文字列を返すようにします。Dictionaryで管理していますから、今チェックしている項目以外の項目にエラー文字列をセットすることは、とても簡単なことです。

    >・入力終了後・ボタン押下時にDBのデータ存在チェックを行う

    この前に、全項目のチェックを一度行うべきでしょう。なぜなら、人が入力していない項目は一度もエラーチェックが行われないからです。

    実際、私は上記の方法で実装を行うことが多いです。


    ★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/

    • 回答の候補に設定 星 睦美 2014年3月20日 7:51
    2014年3月19日 5:45
    モデレータ
  • この場合、BindingGroupを使わない方が見通しが良くなるような気がします。
    各項目のプロパティのSetterから、その項目のエラーチェックを行うようにします。必要があれば他の項目との関連性のチェックも行います。
    エラーの結果は先に述べたようにDictionaryで管理し、IDataErrorInfoのメンバーであるpublic string this[string columnName]は、このDictionaryからエラー文字列を返すようにします。Dictionaryで管理していますから、今チェックしている項目以外の項目にエラー文字列をセットすることは、とても簡単なことです。

    >・入力終了後・ボタン押下時にDBのデータ存在チェックを行う

    この前に、全項目のチェックを一度行うべきでしょう。なぜなら、人が入力していない項目は一度もエラーチェックが行われないからです。

    実際、私は上記の方法で実装を行うことが多いです。

    私も trapemiya さんとほぼ同じ実装です。唯一上記と違うのは、項目に有効値が入力されてない場合はコマンドを実行できないよう、各プロパティのSetter でコマンドが実行可能か検証してることくらいですね。もっともこの辺りの実装は設計にもよるでしょうが・・・

    #というわけで BindingGroup は使ったことがないです。


    ひらぽん http://d.hatena.ne.jp/hilapon/


    2014年3月19日 7:04
    モデレータ
  • trapemiyaさんご回答ありがとうございます。

    イメージですが、ボタン押下時に各項目のチェックの呼び出し+DBチェックを行うようなイメージでしょうか。

    確かにBindingGroupを使うとちょっとコードがみにくくなるなとは思っていたので

    使わないように実装しようと思います。

    ありがとうございます。

    2014年3月19日 9:14
  • イメージですが、ボタン押下時に各項目のチェックの呼び出し+DBチェックを行うようなイメージでしょうか。

    はい。その通りです。このようにしないと、画面を開いた直後にボタンを押下した場合など、例えばデフォルトは空白でも空白ではいけない項目などのチェックを行うことができません。先に触れた、プロパティ名とエラー文をペアで持つDictionaryをチェックし、どのプロパティ値もエラー文が空であれば、初めてDBチェックを行うようにすれば良いでしょう。
    また、Dictionaryをチェックしてエラーがあるかどうかを返すプロパティを用意し、それによってボタンの有効、無効を切り替えるようにしても良いでしょう。具体的にはDataTriggerでボタンのIsEnabledを制御してやります。ただし、最初の1回目は、エラーチェックを行っていないので本当にエラーがあったとしてもボタンが押せてしまいますが、それはそれで仕様的に問題ないと思います。

    ★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/

    • 回答としてマーク VbBeginer001 2014年3月24日 3:59
    2014年3月20日 0:45
    モデレータ
  • trapemiyaさんご回答ありがとうございます。

    エラーをDictionaryで管理しておけば

    だいぶすっきりと作れそうな気がします。助かりました^^

    2014年3月20日 9:05