2013年12月17日火曜日

VB DataGridView と CellValidating

VB の中でも、複雑でとっつき難いのが、DataGridView でしょう。
標準状態に対し、少し、凝った事をしようとすると、途端に、敷居が高くなります。
データ検証もそうですね。
CellValidating と CellValidated について、少し、書いて見ます。


データの入力内容を検証するのに、必ず、説明される CellValidating 。
同様に、行単位で検証する RowValidating 。
   CellValidating と RowValidating イベントは、CellValidating、CellValidated、RowValidating、RowValidated の順となります。
   行を移動しなければ、RowValidating イベントは発生しませんけどね。
   ここでは、RowValidating の説明は省き(避け)ます。
Cell が特定のデータ型で指定されている場合や空白を許可していない場合、変な入力をしようとすると、
DataGridView で例外が発生して、不整合を避ける為、データを受け入れませんね。
DataError イベントをハンドルしなさい と怒られてしまいます。
これを、ユーザーに通知し、再入力を促す為に、Validating が用いられます。  例外回避。
   Validate = 有効かどうか確認する 訳ですね。


良く見掛ける例は、データベースをバインドし、これに空白を許可しない列があった時の、空白入力への対応です。
  msdn 参照 方法 : Windows フォーム DataGridView コントロールのデータを検証する
  この例と同様の方法で、日付のデータで、有り得ない入力 例:2014/01/011111 00:00:00 も対応出来ますね。
これらの例では、行の先頭に 赤く丸い マークのアイコンを表示し、このマークのマウス・フーバー時のテキストを設定します。
カーソルは直前の入力位置に留まっている筈です。
ちゃんと、おめがねにかなうものを入力するまで、このセルから抜け出られなくするのです。
抜け出られる時、つまり、検証で問題なしの時に起こるイベントが Validated です。


最初、VB を弄りだして、戸惑ったのが、実は、これです。
データベースを作り、データを入力したサンプルを作っていて、何故か止まる事がありました。
そう、行先頭のマークに気付けばいいのですが ... 。


これから書くのは、私見です。


留意すべきは、先頭のカラム(行ヘッダ)幅。
DataGridView は、どうしても、横幅が広くなりがちで、先頭カラムも最小幅にしたい所ですから。
私の環境の例では、標準値 Width =  41 より狭くしている場合が多いのですが、
40 より小さくしてしまうと、この、エラー・アイコンが見えなくなってしまいます。  ん~痛し痒し。

日付値等のエラーの場合には、文字が表示されている為、表示色を赤系に変えたりしています。
ただ、空白では、文字がありませんので ... 。
  この場合、敢えて、エラーを引き起こす別の文字列を入れて、注意を喚起するのも ひとつ かと。
  別の方法として、不整合を起こさないがソフト的に有意でない値を事前に決めておき、これを代入。
  後から、その値でフィルタリングする事で、対応・修正する なんて手もありますね。
  または、セルのエラー・テキストを指定してセル自体にエラー・アイコンを出すのも有かな。

      
一例を載せておきます。  汚いコードですがご勘弁。
   日付型で定義した Column (列名に"New"が付くもの) にのみ、検証を行う場合で、入力値はエラー時に赤文字 空白時に"x" を表示
   行頭表示したエラー文字列はCellValidated イベント・ハンドラの中で消去(アイコンも非表示に)
  Private Sub DataGridView1_CellValidating(ByVal sender As Object, ByVal e As System.Windows.Forms.DataGridViewCellValidatingEventArgs) Handles DataGridView1.CellValidating
        'Only Cell Value Changed
        If Not DataGridView1.IsCurrentCellDirty Then
            Exit Sub
        End If
        Dim headerText As String = Me.DataGridView1.Columns(e.ColumnIndex).HeaderText
        If headerText.Contains("New") And Not IsDate(e.FormattedValue) Then
            Me.DataGridView1.Rows(e.RowIndex).ErrorText = Me.DataGridView1.Columns(e.ColumnIndex).Name & " = yyyy/MM/dd HH:mm:ss"
            CType(Me.DataGridView1.EditingControl, TextBox).ForeColor = Color.Crimson
            ' Empty Case , Set "x" To Cell
            If CType(Me.DataGridView1.EditingControl, TextBox).Text = String.Empty Then
                CType(Me.DataGridView1.EditingControl, TextBox).Text = "x"
            End If
            e.Cancel = True
        Else
            CType(Me.DataGridView1.EditingControl, TextBox).ForeColor = Color.Black
        End If
    End Sub

    Private Sub DataGridView1_CellValidated(ByVal sender As Object, ByVal e As System.Windows.Forms.DataGridViewCellEventArgs) Handles DataGridView1.CellValidated
        Me.DataGridView1.Rows(e.RowIndex).ErrorText = String.Empty
    End Sub
コードが見辛くて済みません。 改行・整形など加えず、実コードからコピーしたままですので。 ご勘弁を。


未だに、どうすべきか、悩んでいます。

イベント・ハンドラの中で MessageBox を表示するのも手かとは思いますが、其の度、クリックは うっとおしいかなぁ~とか。

CellValidating では、必要最低限の検証を行い、
先程、触れた、CellValidated や RowValidating や 別のコントロールのイベント・ハンドラで、データ許容値の検証をするのが、今の、私の考え方です。

あるいは、DataGridView 以外のコントロール群で、値設定し、複雑な検証条件をクリアした場合のみ、
DataGridView つまりデータセット等に値をセットで代入させるとか。

それにしても、ユーザー・インターフェイスの部分って、難しいですね。  答がひとつじゃない所



[2013/12/26] この続編を書きました  VB DataGridView と CellValidated

0 件のコメント:

コメントを投稿