VB.NETでDataGridViewをセル編集したときの行数を格納する
DB のテーブルを読み込んだ DataGridView 内のセルが変更された時に、その行数を List に格納しておきます。ここに入れた行数を後で一気に SQL で処理するぞ、という想定。オートインクリメントや主キーのフィールドは変更できない機能も。
関連記事
- DataGridViewへAccessのデータベースファイルを読み込む
- AccessDBテーブルの主キー情報を取得する
- DataGridViewに読み込んだDB情報を再取得する
- DataGridViewをセル編集したときの行数を格納する ←NOW!
- Accessのデータベースファイルへ書き込むための接続・切断
- Accessデータベースのレコードを削除する
- Accessデータベースを更新する
ガッツリ続き物になってしまいました…。過去の分と合わせて順番に読んでいただけると分かりやすいかと思います。
なるべく簡素に書いているので、例外処理は甘いと思われます。ご参考にする際は、ご自分の環境に合わせてご修正ください。
解説のためツギハギしちゃったので、最後の記事(7回め)に全コードまとめてあります。書いたときの環境
- Visual Studio 2010
- .NET Framework 4.0
です。
TargetListクラスを作ってやりとり
DataGridView で変更された行を保持しておくリストをupdateList
、新規に追加された行を保持しておくリストをinsertList
として、それを保持する TargetList クラスを新たに作ります。[プロジェクト] → [クラスの追加] で、TargetList.vb という名前にします。
TargetList.vb
'### TargetList.vb ### Public Class TargetList 'フィールド Private updateList_ As List(Of Integer) Private insertList_ As List(Of Integer) 'ゲッター Public ReadOnly Property updateList() As List(Of Integer) Get Return updateList_ End Get End Property Public ReadOnly Property insertList() As List(Of Integer) Get Return insertList_ End Get End Property '---------------------------------------------------- ' updateListリストに関すること '---------------------------------------------------- Public Function hasUpdateList() As Boolean 'UPDATEリストが存在しているかチェック Dim checkFlg As Boolean = False If Not IsNothing(updateList_) Then checkFlg = True End If Return checkFlg End Function Public Function inUpdateList(ByVal targetRow As Integer) As Boolean 'UPDATEリストに既に行があるかチェック Dim checkFlg As Boolean = False If hasUpdateList() = False Then updateList_ = New List(Of Integer) Else For Each item As Integer In updateList_ If item = targetRow Then checkFlg = True Next End If Return checkFlg End Function Public Sub addUpdateList(targetRow As Integer) 'UPDATEリストへ追加 updateList_.Add(targetRow) End Sub '---------------------------------------------------- ' insertListリストに関すること '---------------------------------------------------- Public Function hasInsertList() As Boolean 'UPDATEリストが存在しているかチェック Dim checkFlg As Boolean = False If Not IsNothing(insertList_) Then checkFlg = True End If Return checkFlg End Function Public Function inInsertList(ByVal targetRow As Integer) As Boolean 'INSERTリストに既に行があるかチェック Dim checkFlg As Boolean = False If hasInsertList() = False Then insertList_ = New List(Of Integer) Else For Each item As Integer In insertList_ If item = targetRow Then checkFlg = True Next End If Return checkFlg End Function Public Sub addInsertList(targetRow As Integer) 'INSERTリストへ追加 insertList_.Add(targetRow) End Sub End Class
updateList
に関して、リストの存在チェック関数、リスト内の該当行チェック関数、リストに行を追加するメソッドを書いておきます(23~48行)。
同様に、insertList
に関して同じような内容を書いたものが 53~78行です。
Form2.vb
Form2を読み込んだとき、新しく作ったクラスについても、インスタンスを作成する記述を追記します。
'### Form2.vb ### Public Class Form2 Private Const FILE_NAME As String = "C:\test.accdb" Private db As DBtable Private list As TargetList Private dgv As DataGridView '---------------------------------------------------- ' メソッド '---------------------------------------------------- Private Sub setTable() 'DataGridViewにテーブル内容をセット db = New DBtable(Me.Text, FILE_NAME) db.setDataSource() list = New TargetList 'TargetListインスタンス生成 dgv.CurrentCell = Nothing End Sub '---------------------------------------------------- ' Formイベント '---------------------------------------------------- '略 '---------------------------------------------------- ' Buttonクリックイベント '---------------------------------------------------- '略 End Class
DBtable.vb
リストを作るときに必要なので、DBtableクラスのほうに、編集されたセルが主キー列、もしくはオートインクリメント列かどうかを判定する関数も書いておきます。
'### DBtable.vb ### Imports System.Data.OleDb Class DBtable '略 '---------------------------------------------------- ' テーブルに関すること '---------------------------------------------------- '略 Public Sub setDataSource() 'テーブルデータ読込(略) End Sub Private Function getTableData(ByVal strSQL As String) As DataTable 'テーブルを接続(略) End Function Public Sub Dispose() 'コネクション切断(略) End Sub Private Sub setColorPrimaryKey() 'データグリッドビューの主キー列色を変える(略) End Sub Public Function isPrimaryKey(ByVal targetColumn As Integer) As Boolean '主キー列か判定 Dim checkFlg As Boolean For Each clm In primaryKeyArray If clm.Ordinal = targetColumn Then checkFlg = True Next Return checkFlg End Function Public Function isAutoIncrement(ByVal targetColumn As Integer) As Boolean 'オートインクリメント列か判定 Dim checkFlg As Boolean If tableData.Columns(targetColumn).AutoIncrement = True Then checkFlg = True Return checkFlg End Function End Class
前回のコードに上記ハイライト部分の、2つのプロシージャを加えます。
エラーを見やすくしておく
変更行の格納をする前に。
セル編集時、型が違うとか、何かしら条件に一致しない場合、DataGridView からエラーを出されるときがあるんです。
こんな感じで、Int型を指定したフィールドに文字列を入れたりすると、
ぎゃー!って言いたくなるようなエラー画面w これ、開発者ならまだ良いですけどユーザーさんにはあんまり見せたくないです…。ので、
Form2.vb
DataErrorイベントというのを利用して、エラーが起きた位置と、エラー内容をもっとわかりやすく示してあげます。
'### Form2.vb ### Public Class Form2 '略 '---------------------------------------------------- ' メソッド '---------------------------------------------------- Private Sub setTable() 'DataGridViewにテーブル内容をセット(略) End Sub '---------------------------------------------------- ' Formイベント '---------------------------------------------------- Private Sub Form2_Load(sender As System.Object, e As System.EventArgs) Handles Me.Load 'フォームロード時(略) End Sub Private Sub Form2_FormClosed(sender As Object, e As System.Windows.Forms.FormClosedEventArgs) Handles Me.FormClosed 'フォームが閉じたとき(略) End Sub '---------------------------------------------------- ' Buttonクリックイベント '---------------------------------------------------- Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click '再読込ボタン(Button1)クリック時(略) End Sub '---------------------------------------------------- ' DataGridViewイベント '---------------------------------------------------- Private errFlg As Boolean Private Sub DataGridView1_DataError(sender As Object, e As System.Windows.Forms.DataGridViewDataErrorEventArgs) Handles DataGridView1.DataError '編集でエラーが発生した時 If Not (e.Exception Is Nothing) Then MessageBox.Show(e.ColumnIndex + 1 & "列 " & e.RowIndex + 1 & "行目 のセルでエラーが発生しました。" & vbCrLf & vbCrLf & e.Exception.Message, "エラー", MessageBoxButtons.OK, MessageBoxIcon.Error) End If e.Cancel = False '値を戻す errFlg = True 'エラーフラグを立てる End Sub End Class
エラーになった場合、35行目のプロシージャを通るので、オリジナルのエラーメッセージを出して値を元へ戻します。列も行も 0 からの値になるので、ユーザーにわかりやすいよう、それぞれ +1 した場所をメッセージとして出してみます。
こちらを参考にさせていただきました。
41行目は、この後「セル編集後」というイベント処理を走らせる予定なので、エラー発生時はそちらを回避するためにフラグを立てておきます。
これで、さっきと同じことをすると、
さっきよりも分かりやすいエラーメッセージが出て、
値も元に戻る、という。
セルが編集されたら各 List に格納
ではメインのほうのイベントを書きます。
'### Form2.vb ### Public Class Form2 '略 '---------------------------------------------------- ' メソッド '---------------------------------------------------- Private Sub setTable() 'DataGridViewにテーブル内容をセット(略) End Sub Private Sub undoValue() '元の値に戻す dgv.CurrentCell.Value = cellValue End Sub '---------------------------------------------------- ' Formイベント '---------------------------------------------------- Private Sub Form2_Load(sender As System.Object, e As System.EventArgs) Handles Me.Load 'フォームロード時(略) End Sub Private Sub Form2_FormClosed(sender As Object, e As System.Windows.Forms.FormClosedEventArgs) Handles Me.FormClosed 'フォームが閉じたとき(略) End Sub '---------------------------------------------------- ' Buttonクリックイベント '---------------------------------------------------- Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click '再読込ボタン(Button1)クリック時(略) End Sub '---------------------------------------------------- ' DataGridViewイベント '---------------------------------------------------- Private errFlg As Boolean Private insertFlg As Boolean Private cellValue Private Sub DataGridView1_DataError(sender As Object, e As System.Windows.Forms.DataGridViewDataErrorEventArgs) Handles DataGridView1.DataError '編集でエラーが発生した時(略) End Sub Private Sub DataGridView1_UserAddedRow(sender As Object, e As System.Windows.Forms.DataGridViewRowEventArgs) Handles DataGridView1.UserAddedRow '新しい行が追加された時 insertFlg = True End Sub Private Sub DataGridView1_CellBeginEdit(sender As Object, e As System.Windows.Forms.DataGridViewCellCancelEventArgs) Handles DataGridView1.CellBeginEdit 'セル編集前 errFlg = False insertFlg = False cellValue = dgv.CurrentCell.Value '該当セルの値を格納しておく End Sub Private Sub DataGridView1_CellEndEdit(sender As Object, e As System.Windows.Forms.DataGridViewCellEventArgs) Handles DataGridView1.CellEndEdit 'セル編集後 If errFlg = False Then 'オートインクリメントの列は変更不可 If db.isAutoIncrement(dgv.CurrentCell.ColumnIndex) = True Then MessageBox.Show("このセルはオートインクリメントなので変更できません", "エラー", MessageBoxButtons.OK, MessageBoxIcon.Error) Call undoValue() If insertFlg = False Then Exit Sub '既存レコードだった場合はここで終了 End If '新規レコードだった場合 If insertFlg = True Then If list.inInsertList(dgv.CurrentRow.Index) = False Then 'INSERTリストチェック list.addInsertList(dgv.CurrentRow.Index) 'なければINSERTリストに追加 End If Else '既存レコードだった場合 If list.inInsertList(dgv.CurrentRow.Index) = False Then 'INSERTリストになければ '主キーなら If db.isPrimaryKey(dgv.CurrentCell.ColumnIndex) = True Then MessageBox.Show("このセルは主キーなので変更できません", "エラー", MessageBoxButtons.OK, MessageBoxIcon.Error) Call undoValue() Exit Sub End If 'チェックして追加 If list.inUpdateList(dgv.CurrentRow.Index) = False Then 'UPDATEリストチェック list.addUpdateList(dgv.CurrentRow.Index) 'なければUPDATEリストに追加 End If End If End If '変更したセルに色をつける dgv.CurrentCell.Style.BackColor = Color.LavenderBlush End If End Sub End Class
39行目、insertFlg
という変数は、46行目のUserAddedRow
イベントでフラグを立てておき、新規行か否かの判定に使います。
40行目のcellValue
という変数は、51行目のCellBeginEdit
イベントで、セルの編集前の値を保持しておきます。編集されたくないセルだった場合に、元に戻すためです。ここで一緒に各フラグも初期化しておきます。
値を元に戻すプロシージャは、12行目です。
メインは58行目のCellEndEdit
イベント。もしエラーになるような値だった場合、ここより先にDataError
イベントを通るので、エラーフラグが立っているときは処理しません(60行)。
オートインクリメント、主キーかどうかなどを、さっきDBtable.vb
クラスに書いた関数などを使ってチェックしながら、新規/既存レコードを判別して List に該当行を格納していきます。
最後に、ユーザーに分かりやすいように変更したセルの色を変えておくといいかも(90行)。
これで実行してみて、
適当にセルの内容を変えると、updateList
、insertList
に該当の行数が格納され(行数はゼロから始まります)、このように変更したセルの色が変わります。
主キーのセルが変更された場合は以下のようになるはず。
編集して隣のセルに移動しようとすると、
ダメですよーって言われて、
メッセージボックスを閉じると値が戻ります。
'### Form2.vb ### Public Class Form2 '略 '---------------------------------------------------- ' メソッド '---------------------------------------------------- '略 '---------------------------------------------------- ' Formイベント '---------------------------------------------------- Private Sub Form2_Load(sender As System.Object, e As System.EventArgs) Handles Me.Load 'フォームロード時(略) End Sub Private Sub Form2_FormClosing(sender As Object, e As System.Windows.Forms.FormClosingEventArgs) Handles Me.FormClosing 'フォームが閉じるとき '未保存データ確認 If list.hasUpdateList = False And list.hasInsertList = False Then Exit Sub End If If MessageBox.Show("未保存のデータがあります。フォームを閉じてよろしいですか?", "確認", MessageBoxButtons.OKCancel) <> 1 Then e.Cancel = True End If End Sub Private Sub Form2_FormClosed(sender As Object, e As System.Windows.Forms.FormClosedEventArgs) Handles Me.FormClosed 'フォームが閉じたとき(略) End Sub '---------------------------------------------------- ' Buttonクリックイベント '---------------------------------------------------- '略 '---------------------------------------------------- ' DataGridViewイベント '---------------------------------------------------- '略 End Class
ついでに、編集したセルがある(Listに格納されている行がある)のにフォームを閉じようとしたときのために、FormClosing
イベントに確認メッセージを書いておくといいかも(17~26行)。
まだ SQL まで到達していない…。先が長そうな予感w
コメントは承認制ですので、反映までしばらくお待ち下さい。(稀にスパムの誤判定にて届かないこともあるようですので、必要な際はお問い合わせからお願い致します。)
YouTubeでQ&Aコンテンツを企画しています
運営しているYouTubeチャンネルで、ご相談やご質問を募集しています。動画のコメントやお問い合わせページからお気軽にご相談をお寄せください。