[ADO.NET] DataGridView 内のデータ編集とそのときのバインドした DataTable の動作

佐藤 美菜
SQL Developer Support Engineer

みなさん、こんにちは。今回は、DataGridView に DataTable をバインドしているときに DataGridView 内のデータを編集した際の動作について、少々注意が必要な場合をご紹介します。

現象

以下のような操作をします。

  1. DataGridView に DataTable をバインドして画面表示をしている
  2. DataGridView の値を変更する
  3. DataTable(DataSet) へ操作を行う ( 例えば、DataTable.Select、DataTable.Compute、DataTable.Copy 等)

この場合、DataGridView で変更した値は、明示的にカレント行を移動しないと DataTable には反映されません。
これは DataGridView の想定された動作です。

詳細 

例えば、下記のように「全選択」ボタンで DataGridView のすべての行の col1 をチェックします。そして、チェックされた行数を DataTable.Compute メソッドでカウントします。すると、3 行選択されているにも関わらず、2 行しかカウントされません。

image

確認コード例(VB)  

Public Class Form1

    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load        ‘ データソースから取得した値の DataTable を DataGridView へバインドします        Me.TableAdapter1.Fill(Me.DataSet1.Table_1)         DataGridView1.DataSource = Me.DataSet1.Table_1

    End Sub                 Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click        For i As Integer = 0 To DataGridView1.RowCount - 1             DataGridView1.Item("Col1DataGridViewCheckBoxColumn", i).Value = 1         Next

      MessageBox.Show("選択数:" & DataSet1.Table_1.Compute("COUNT(col1)", "col1<>0") & "行選択されました。")     End Sub

End Class

DataGridView の値を更新すると、カレント行を移動するまでその更新は保留のままになります。今回のように、DataGridView への更新が GUI からではなく、プログラム中から行われた場合はカレント行が移動されません。上記の例では、カレント行である DATA1 の行が保留のままになっています。変更が確定されていないため、DataTable からは意図した結果が得られない状況になります。

DataGridView のカレント行を移動すれば、保留だった行の変更が確定され、DataTable からも結果が確認できるようになるのですが、今回のようにプログラム中から更新する場合には少し対策が必要です。

対策

対策としては、以下の 3 つの方法が考えられます。

  • DataGridView の値を更新後、明示的に DataRow.EndEdit を実行する
  • DataGridView の値を更新後、明示的に DataTable.AcceptChanges ( または DataSet.AcceptChanges ) を実行する
  • DataGridView の値を更新する代わりに直接 DataRow を更新する

 

対策1: DataRow.EndEdit

DataRow.EndEdit をカレント行に対して行うことにより、この行で行われている変更を終了し確定します。

上記コードでの対策例   

Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click     For i As Integer = 0 To DataGridView1.RowCount - 1                      DataGridView1.Item("Col1DataGridViewCheckBoxColumn", i).Value = 1     Next

    '対処策1     Me.DataSet1.Table_1.Rows(DataGridView1.CurrentCell.RowIndex).EndEdit()

    MessageBox.Show("選択数:" & DataSet1.Table_1.Compute("COUNT(col1)", "col1<>0") & "行選択されました。") End Sub

 

対策2: DataTable.AcceptChanges

AcceptChanges を実行すると、カレント行を含めた、前回 AcceptChanges を呼び出した以降に行われた DataTable への変更を確定します。  このとき DataRowState も変更され、すべての Added および Modified 行は、Unchanged に変更され、Deleted 行は削除されます。

※ DataTable 全体に対して変更が確定され、RowState が Unchanged に変更される点が「対策1 : DataRow.EndEdit」 と異なる点です。    

上記コードでの対策例

Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click     For i As Integer = 0 To DataGridView1.RowCount - 1                      DataGridView1.Item("Col1DataGridViewCheckBoxColumn", i).Value = 1     Next

    '対処策2     Me.DataSet1.Table_1.AcceptChanges()

    MessageBox.Show("選択数:" & DataSet1.Table_1.Compute("COUNT(col1)", "col1<>0") & "行選択されました。") End Sub

 

対策3: DataRow を直接更新する

DataGridView の値を更新する代わりに直接 DataRow を更新すると、DataGridView で表示される値と DataTable の値との間で差が出ないため、意図した結果を得られます。

上記コードでの対策例   

Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click     '対策3 'For i As Integer = 0 To DataGridView1.RowCount - 1 ' DataGridView1.Item("Col1DataGridViewCheckBoxColumn", i).Value = 1 'Next     For i As Integer = 0 To Me.DataSet1.Table_1.Rows.Count - 1         Me.DataSet1.Table_1.Rows(i)(0) = 1     Next

    MessageBox.Show("選択数:" & DataSet1.Table_1.Compute("COUNT(col1)", "col1<>0") & "行選択されました。") End Sub

 

どの対策を採用するかは、アプリケーションの要件に依存しますので、要件にあった方法をご選択ください。

 

<参考情報>
DataRowView クラス
https://msdn.microsoft.com/ja-jp/library/system.data.datarowview.aspx

DataRow.EndEdit メソッド
https://msdn.microsoft.com/ja-jp/library/system.data.datarow.endedit.aspx

DataTable.AcceptChanges メソッド
https://msdn.microsoft.com/ja-jp/library/system.data.datatable.acceptchanges.aspx

DataRowState 列挙体
https://msdn.microsoft.com/ja-jp/library/system.data.datarowstate.aspx