インフラジスティックス・ジャパン株式会社のBlog

インフラジスティックス・ジャパン株式会社のチームメンバーが技術トレンド、製品Tips、サポート情報からライセンス、日々の業務から感じることなど、さまざまなトピックについてお伝えするBlogです。

WinGrid 活用術 2 - バリデーション

こんにちは!前回の データの表示/登録 に続いて、今回は WinGrid におけるバリデーション方法をご紹介します。

バリデーションというのは、要するに入力項目の審査のことですね。例えば単純な最大値や最大桁数や禁則文字のチェックであったり、別の項目がAの時はBしか入力できないなどといった相関チェックであったり、同じ値を登録させないような重複チェックであったり・・・とにかくアプリケーションとしてユーザに登録させたくないデータが入力されたときに、それは登録できないんだよ、と伝える仕組みのことです。
今回は WinGrid の各セルに対する入力チェックのかけ方をいくつかご紹介します。

 

プロパティ設定による入力制限

まず、プロパティで設定できる基本的な入力制限を紹介します。
WinGridの各列のバインド列はデータソースの対応するカラムのタイプに応じて自動的に適切な(数値型や文字型や日付型など)データタイプが設定され、例えば数値項目であれば数字と小数点以外入力できなかったり、日付項目であれば実在する日付しか入力できなくなったりと、データタイプに応じて自動的に入力制限がかかります。(もちろん、設定次第では解除することも他の入力制限をかけることもできます。)
これは、バインドした項目のデータタイプに応じてセル編集時に利用されるエディターが、数値編集用であったり日付編集用のものに自動的に変わるためです。この編集時に利用されるエディターに対してマスク処理などを施すこともできますが、以下のコードのように各列毎に設定できるプロパティを利用することで簡単に入力制限をかけられるものもあります。

            //最小値、最大値の設定(※数値項目、日付項目の場合のみ有効)
            ultraGrid1.DisplayLayout.Bands[0].Columns["Number"].MinValue = 0;
            ultraGrid1.DisplayLayout.Bands[0].Columns["Number"].MaxValue = 99999;

            //最大文字数(※文字列項目の場合のみ有効)
            ultraGrid1.DisplayLayout.Bands[0].Columns["String1"].MaxLength = 20;

            //正規表現を使った文字制限
            ultraGrid1.DisplayLayout.Bands[0].Columns["String1"].RegexPattern = "^[a-zA-Z0-9!-/:-@¥[-`{-~]+$";

最小値、最大値、最大文字数の設定と、正規表現を使った入力制限をかけています。この正規表現を使った方法で入力制限をかけると、以下のように入力自体はできるもののエラーメッセージがダイアログ表示されフォーカスアウトできないというような挙動となります。※この時、CellDataError イベントをハンドルすることでフォーカスをとどめるかどうかや、元の値に戻すかどうかなど細かい制御を加えることができます。(この辺り、標準の DataGridView と一緒ですね。

image

 

イベントハンドリングによる入力制御

セル編集時のイベントハンドリングによるバリデーションは大きく分けて2パターン考えられます。

1つ目はセル編集が終わりフォーカスを抜けようとする際にチェックを行い、エラーであった場合はその場でダイアログ表示を行い、エラーを修正するまでは編集状態を抜けられないようにする。

2つ目はセル編集を抜けて(内部的に)セルの値をコミットしにいくタイミングでチェックし、エラーであった場合その旨をユーザへ通知し、(場合によっては値を元の正常な値に戻して)フォーカスを抜けるというものです。

image

どちらも一長一短で、前者はエラーであった場合でもフォーカスが留まるのでその場ですぐに修正する場合は使いやすいのですが、エラー値でもいいからフォーカスを抜けたいときに抜けられない、スピード重視の連続入力が行ないえないというデメリットがあります。おそらく多くの方が経験されたことがあるかと思いますが、入力エラーでフォーカスが抜けられないがために画面を閉じることすら許されないという悲惨な状況を生みかねないため、個人的には後者のほうが良いと思っています。(後者の場合はどのようにユーザにエラーを伝えるかがポイントですね!)
それぞれ実装方法をチェックいていきます。

フォーカス抜けられなくするエラー告知のコード例

        //セルの編集から抜け出るときにチェック(この場合、エラーを改善しない限りフォーカスを抜けられない)
        void ultraGrid1_BeforeExitEditMode(object sender, Infragistics.Win.UltraWinGrid.BeforeExitEditModeEventArgs e)
        {
            // String1列に"a"が含まれていないかどうかチェック
            if (ultraGrid1.ActiveCell.Column.Key == "String1")
            {
                string text = ultraGrid1.ActiveCell.EditorResolved.CurrentEditText;
                if (text.Contains("a"))
                {
                    MessageBox.Show(text + " は \"a\" を含んでいるため入力できません。");
                    e.Cancel = true;
                }
            }
        }

フォーカスを抜けられるエラー告知のコード例

        //セルの編集から抜け出た後にチェック(この場合、値が変更間のものに戻り、フォーカスは次へ遷移する)
        void ultraGrid1_BeforeCellUpdate(object sender, Infragistics.Win.UltraWinGrid.BeforeCellUpdateEventArgs e)
        {
            // String1列に"a"が含まれていないかどうかチェック
            if (e.Cell.Column.Key == "String1"){
                if(e.NewValue.ToString().Contains("a")){
                    MessageBox.Show(e.NewValue.ToString() + " は \"a\" を含んでいるため入力できません。");
                    e.Cancel = true;
                }
            }
        }

後者の場合、「e.Cancel=true;」により値をロールバックさせていますが、場合によっては苦労して入力したデータがロールバックで消えてしまうことでユーザへ大変なストレスを与える事もあります。また、各セルの入力エラーごとにダイアログを出力しているとスピード重視で連続セル入力を行っている場合は、こちらも大変なストレスになります。
そこで、もう一つエラーダイアログを出さずに、また入力した値をの残しつつエラーが存在することを伝える方法として、バインドしている DataTable の ErrorInfo を利用してセルにエラー表示する方法をご紹介します。

 

DataTable の ErrorInfo を使ったエラー表示

まず、DataErrorInfoをセル単位または行単位とセル単位で有効に設定します。

            // DataErrorInfoを利用する。
            ultraGrid1.DisplayLayout.Override.SupportDataErrorInfo = Infragistics.Win.UltraWinGrid.SupportDataErrorInfo.RowsAndCells;

続いて、BeforeCellUpdate イベントでダイアログ表示していた箇所を以下のように変更します。

        //セルの編集から抜け出た後にチェック(この場合、値が変更間のものに戻り、フォーカスは次へ遷移する)
        void ultraGrid1_BeforeCellUpdate(object sender, Infragistics.Win.UltraWinGrid.BeforeCellUpdateEventArgs e)
        {
            //// String1列に"a"が含まれていないかどうかチェック
            //if (e.Cell.Column.Key == "String1"){
            //    if(e.NewValue.ToString().Contains("a")){
            //        MessageBox.Show(e.NewValue.ToString() + " は \"a\" を含んでいるため入力できません。");
            //        e.Cancel = true;
            //    }
            //}

            // String1列に"a"が含まれていないかどうかチェック
            if (e.Cell.Column.Key == "String1")
            {
                if (e.NewValue.ToString().Contains("a"))
                {
                    // セルにエラー貼り付け
                    e.Cell.Row.DataErrorInfo.SetColumnError(
                        e.Cell.Column.Key,
                        e.NewValue.ToString() + " は \"a\" を含んでいるため入力できません。"
                        );
                }
                else
                {
                    // セルにエラー解除
                    e.Cell.Row.DataErrorInfo.SetColumnError(e.Cell.Column.Key, null);
                }
            }
        }

対象の行の DataErrorInfo から SetColumnError() メソッドでセルに対してエラー情報を貼り付けています。また、忘れてはいけないのがエラーの解除で、エラーではなかった場合にエラー情報をクリアしてあげないと正常値を入力いてもいつまでたってもエラー表示が残ってしまいます。

実行結果がこちら。

image

入力エラーのセルが一目瞭然ですね。!マークのところにマウスカーソルを合わせると上図のようにチップテキストとして設定したエラーメッセージが表示されます。
Webの入力フォームで入力エラーだった場合に項目の右隣か下に赤文字でエラーメッセージが表示されるものをよく見かけますが、これならスムーズに入力を続けられますね。

続いてエラーセルをより分かりやすくするためにエラーセルの外観を変更してみます。WinGridでは外観変更には Appearance クラスを変更しますが、エラーセルの外観は DataErrorCellAppearance により変更できます。

            //エラーセルの外観変更()
            Infragistics.Win.Appearance ap = (Infragistics.Win.Appearance)ultraGrid1.DisplayLayout.Override.DataErrorCellAppearance;
            ap.BackGradientStyle = Infragistics.Win.GradientStyle.VerticalBump; //グラデーション設定
            ap.BackColor = Color.FromArgb(255, 181, 193); //ピンク
            ap.BackColor2 = Color.White; //白

image

入力エラーのあるセルの所在がかなり見やすくなりましたね。

最後に、確定ボタン押した時のチェックについてです。
フォーカスアウトのタイミングで実装している入力チェックを再度同じロジックを通してチェックするという方法でもよいのですが、DataErrorInfo を利用している場合は、以下のように書くだけで入力したタイミングで行っていたチェック結果を利用してエラー有無を確認することができます。

以下のコードでは、先頭のエラー情報を拾ってダイアログ表示を行っています。

        // *****************************
        // 確定ボタン押下
        // *****************************
        private void ultraButton3_Click(object sender, EventArgs e)
        {

            // バインドしている DataTable の取り出し
            DataTable dt = ultraGrid1.DataSource as DataTable;

            // エラーが存在しないかどうか確認
            DataRow[] rows = dt.GetErrors();
            if (rows.Length != 0)
            {
                //存在する場合は先頭のエラー情報を取得
                String msg = rows[0].GetColumnError(rows[0].GetColumnsInError()[0]);
                MessageBox.Show(msg); //エラー表示
                return;
            }

            // DB更新確認
            if (MessageBox.Show(
                "編集内容を確定しますか?",
                "登録確認",
                MessageBoxButtons.YesNo,
                MessageBoxIcon.Question)
                == DialogResult.No)
            {
                return;
            }
            
            // ・・・省略

image

実はこれは.NET標準の DataGridView でも同じような事ができるのですが、WinGrid を使って実装するとどんな感じになるのかという紹介をさせていただきました。また、何よりこの ErrorInfo を使ったバリデーションはユーザにも喜ばれるしコード量も減らせるとても良いものなので、使ったことないという方がいらっしゃれば是非この記事を参考に試していただければと思います。

次回はWinGridの外観のカスタマイズについてご紹介しようと思います。

今回作成したサンプルのダウンロードは こちら から

 

無料トライアルダウンロードはこちら

jp.infragistics.com