データバインディングにおけるデータの検証

Last Updated 2012/11/23


ユーザーにデータの入力を要求する場合、データの正否のチェックを欠かすことはできません。このページではデータバインディングにおけるデータの検証を取り上げます。


ユーザーにデータの入力を要求する場合の UI(ユーザーインターフェース)として、データを間違えて入力する可能性を極力少なくなるように配慮することがデータ検証の極意です。たとえば、住所を入力する場合は都道府県名をコンボボックスの中から選択させるなどです。しかし、あらかじめ予想できないデータを入力する場合、入力間違いは常に発生しえますので、データの検証を避けることはできません。

WPF はデータバインディングにおけるデータの検証のために多くの機能を提供します。ここではできるだけ実務的に説明したいと思います。なお、このページで出てくるクラスのプロパティやメソッドの詳細は説明しません。私が公開している WPFClass を参照してください。


DataErrorValidationRule クラス

先に「できるだけ実務的に説明」と書きました。そのためにはできるだけ WPF が提供する機能を使うことがコーディング量を節約する道だと考え、DataErrorValidationRule クラスと IDataErrorInfo インターフェースとを使う手順を最初に取り上げることにしました。

DataErrorValidationRule クラスは ValidationRule クラスから派生するクラスで、IDataErrorInfo インターフェースを実装するデータ構造と組み合わせます。Binding クラスの ValidatesOnDataErrors プロパティを true に設定するだけで DataErrorValidationRule クラスのインスタンスを作成する必要はありません。

Note WEB 上で公開されているサンプルプロジェクトの中には DataErrorValidationRule クラスのインスタンスを作成するコードを書いているものがありますが正しい手順ではありません。作成してもかまわないのですが、WPF の機能を使っているとはいえません。

このクラスと似た位置づけになっている ExceptionValidationRule クラスについて触れておきます。このクラスはデータの型変換時に何らかの例外が発生したときを捕捉し、エラーの発生をユーザーに知らせる機能を持ちます。

Note ExceptionValidationRule クラスも ValidationRule クラスから派生するクラスで、Binding クラスの ValidatesOnExceptions プロパティに true を指定すると自動的にインスタンスが作成されます。

WPF はデータ型の変換を自動的にやってくれます。たとえば、元のデータ型が int 型であってもそのデータをテキストボックスに表示するときは自動的に文字列に変換するといった具合です。しかし、万が一例外の捕捉に失敗したときのために ExceptionValidationRule クラスを有効にしておくとより安全といえるかもしれません。

IDataErrorInfo インターフェース

このインターフェースを実装するクラスは DataErrorValidationRule クラスと組み合わせることで効果を発揮すると先に書きました。

このインターフェースは 2 つのプロパティしか持ちません。Error プロパティと [] 演算子(WPF のヘルプでは Item プロパティとなっていますが、このようなプロパティは実在しません)だけです。Error プロパティは不要なら null を返せばいいでしょう。主役は [] 演算子です。いずれにしろ、このインターフェースの実装方法はサンプルプロジェクトを参照してください。

下図の上は、データを DataGrid コントロールに表示したところです。フォーム右側の [編集] ボタンをクリックすると下の図のような行のデータを編集するフォームを表示します。


下の図の [価格] のところに赤色の [?] が表示されていますが、これは価格の 1,000 円以下の制限を超えているからです。また、ツールチップ形式でエラーの内容を表示しています。以下は、このプロジェクトで設定したデータの検証基準を示します。

次は、サンプルプロジェクトのソースファイルです。Visual Studio 2010 C# / WPF で作りました。

ValidationTest.zip (11,958 bytes)

ValidationRule クラス

DataErrorValidationRule クラスは ValidationRule クラスから派生するクラスだと説明しましたが、ここでは ValidationRule クラスから派生する独自のクラスを作成する場合を取り上げます。

ValidationRuleTest.zip (12,341 bytes)

サンプルプロジェクトを実行してもらえば分かりますが、実行手順も結果も「DataErrorValidationRule クラス」の例とまったく同じです。


Validation クラス

ここでチョット Validation クラスについて触れておこうと思います。上記の二つのサンプルプロジェクトの中ですでに使っていますので、気付いていると思いますが、XAML 構文の中でデータの検証にかかわる添付プロパティを提供します。以下に、プロパティをリストアップしておきます。

最初の三つのプロパティは上記のサンプルプロジェクトで使っています。うしろの二つは同じフォーム内でソース側とターゲット側が共存する場合にエラー表示をどちら側で行うかを指定するものです。たとえば、バインディングソースが TextBox コントロールの Text プロパティ、バインディングターゲットが Label コントロールの Content プロパティだとし、TextBox のほうに Validation.ValidationAdornerSite プロパティとして Label コントロールを指定すると、検証エラーの結果はラベル側で表示されます。また、Validation.ValidationAdornerSiteFor プロパティは自動的に TextBox コントロールになります。


BindingGroup クラス

上記の二つのサンプルプロジェクトでは個々のデータを単独で検証しました。では、複数のプロパティが互いに関連する場合はどうでしょう。ここでは、価格が 1,000 円未満の場合、数量は 10 個以上でなければならないとします。このような場合のために BindingGroup クラスが用意されています。

BindingGroup クラスという名前から想像すると複数のバインディングをグループ化するような印象を受けますが、複数のバインディングではなく、それらのデータの検証をグループ化するという意味です。

さて、BindingGroup クラスは名前もまぎらわしのですが、使い方もチョットややこしい。このクラスのインスタンスを設定または取得するプロパティは FrameworkElement クラスに与えられているところに注意してください。BindingGroup はデータの検証をグループ化することが目的ですから必然的に複数のコントロールをまとめて管理しなければなりません。そこで、BindingGroup 専用のコンテナコントロールを提供するのではなく、FrameworkElement コントロール内にグループ化対象のコントロールを配置することにしたのでしょう。

次のコードは、StackPanel コントロール内にグループ化対象のコントロールを配置する例です。

<StackPanel Name="stackPanel" Orientation="Vertical" Validation.Error="stackPanel_Error">
  <StackPanel.BindingGroup>
    <BindingGroup NotifyOnValidationError="True">
      <BindingGroup.ValidationRules>
        <local:OrderValidationRule ValidationStep="ConvertedProposedValue" />
      </BindingGroup.ValidationRules>
    </BindingGroup>
  </StackPanel.BindingGroup>

  ....

コードのポイントだけ説明しておきます。BindingGroup の NotifyOnValidationError プロパティに true を設定すると、エラーが発生したとき、Validation.Error 添付イベントが発生します。コード例では、stackPanel_Error イベントですね。

Validation.Error イベントの典型的なコード例を以下に示します。

private void stackPanel_Error(object sender, ValidationErrorEventArgs e)
{
  if (e.Action == ValidationErrorEventAction.Added)
  {
    MessageBox.Show(e.Error.ErrorContent.ToString(), "検証エラー", MessageBoxButton.OK, MessageBoxImage.Warning);
    stackPanel.BindingGroup.BeginEdit();
  }
}

下図は、stackPanel_Error イベント内で表示するメッセージボックスです。

上記のコード例の中で注意すべきことがもう一点あります。OrderValidationRule オブジェクトの ValidationStep プロパティです。ここでは ConvertedProposedValue に設定しましたが、これが最適かどうかは場合によります。しかし、どのような場合に何を設定すればいいかはテストしていませんので、これ以上の説明は省略します。

以下は、BindingGroup をテストするアプリケーションです。Visual Studio 2010 C# / WPF で作りました。フリーウエアですからご自由にお使いください。

ValidationGroupTest.zip (12,528 bytes)

−以上−