VB.NETでDataGridViewをセル編集したときの行数を格納する

VB.NETでDataGridViewをセル編集したときの行数を格納する

DB のテーブルを読み込んだ DataGridView 内のセルが変更された時に、その行数を List に格納しておきます。ここに入れた行数を後で一気に SQL で処理するぞ、という想定。オートインクリメントや主キーのフィールドは変更できない機能も。


関連記事

  1. DataGridViewへAccessのデータベースファイルを読み込む
  2. AccessDBテーブルの主キー情報を取得する
  3. DataGridViewに読み込んだDB情報を再取得する
  4. DataGridViewをセル編集したときの行数を格納する ←NOW!
  5. Accessのデータベースファイルへ書き込むための接続・切断
  6. Accessデータベースのレコードを削除する
  7. 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 からエラーを出されるときがあるんです。

150223-6

こんな感じで、Int型を指定したフィールドに文字列を入れたりすると、

150223-7

ぎゃー!って言いたくなるようなエラー画面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行目は、この後「セル編集後」というイベント処理を走らせる予定なので、エラー発生時はそちらを回避するためにフラグを立てておきます。

150223-6

これで、さっきと同じことをすると、

150223-8

さっきよりも分かりやすいエラーメッセージが出て、

150223-9

値も元に戻る、という。

セルが編集されたら各 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行)。

150223-1

これで実行してみて、

150223-2

適当にセルの内容を変えると、updateListinsertListに該当の行数が格納され(行数はゼロから始まります)、このように変更したセルの色が変わります。

主キーのセルが変更された場合は以下のようになるはず。

150223-3

編集して隣のセルに移動しようとすると、

150223-4

ダメですよーって言われて、

150223-5

メッセージボックスを閉じると値が戻ります。

'### 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

公開日:2015/02/23

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください

コメントは承認制ですので、反映までしばらくお待ち下さい。(稀にスパムの誤判定にて届かないこともあるようですので、必要な際はお問い合わせからお願い致します。)