שתף באמצעות


Force DataGridView to Update Underlying DataTable

Question

Tuesday, March 27, 2012 9:52 PM

I need to force a DataGridView cell to update the underlying DataTable.  Here is my situation.  I fill a DataTable using my adapters Fill method, then set that DataTable as my DataGridView's datasource.  Lets say one of my DataGridView columns is a CheckBox column.  If I left-click a checkbox in the first row and then left click a different row in the DataGridView the underlying DataTable_RowChanged event fires, which tells me the first edit I did has gone to the underlying DataTable.  

Here is the problem.  If I left-click the checkbox in a row and then right-click the column header to display my context menu the DataTable_RowChanged event doesn't fire because the DataGridView row is still in edit mode.  I've tried Me.DataGridView.EndEdit(), but that doesn't cause my RowChanged event to fire, why?

I need to ensure my underlying DataTable matches the data in the DataGridView, because I am getting a subset of my original table.  See code below.

Private Sub DataGridView1_MouseDown(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles DataGridView1.MouseDown

   ' run this block of code if right-clicked
   If e.Button = Windows.Forms.MouseButtons.Right Then

       ' ensure the datagridview data matches the underlying datatable before proceeding
       If CType(sender, DataGridView).IsCurrentRowDirty Then Me.DataGridView1.EndEdit()

   ' get a subset of the original datatable, where CheckBox column is True
   Dim dv As DataView = CType(Me.DataGridView1.DataSource, DataTable).AsDataView
   dv.RowFilter = "ISNULL(CheckBoxColumn, False) = True"
   Dim dt As DataTable = dv.ToTable
   Me.DataGridView1.DataSource = dt

    ' call datagridview column context menu
    Me.ShowContextMenu(sender, e, Me.flpPending)

    End If

End Sub

Thanks in advance,

Ryan

All replies (5)

Wednesday, March 28, 2012 10:27 PM ✅Answered | 1 vote

Hi Ryan,

Give this code a spin and see if it gives you the behavior you are looking for.

Public Class Form1

   Private WithEvents dt As New DataTable
   Private WithEvents dgv As New DataGridView
   Private WithEvents cm As New ContextMenuStrip
   Private WithEvents Item1 As New ToolStripButton With {.Text = "Item1"}

   Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
      Me.Size = New Size(400, 300)
      With dt
         .Columns.Add(New DataColumn("check1", GetType(Boolean)))
         .Columns.Add(New DataColumn("check2", GetType(Boolean)))
         .Columns.Add(New DataColumn("text1", GetType(String)))
      End With
      cm.Items.Add(Item1)
      dgv.Size = New Size(375, 250)
      dgv.DataSource = dt
      dgv.EditMode = DataGridViewEditMode.EditOnEnter
      dgv.ContextMenuStrip = cm
      Me.Controls.Add(dgv)

   End Sub

   Private Sub cm_Opening(ByVal sender As Object, ByVal e As System.ComponentModel.CancelEventArgs) Handles cm.Opening
      If dgv.CurrentRow.IsNewRow Then Exit Sub
      If dgv.CurrentCell.IsInEditMode Then
         dgv.EndEdit()
         CType(dgv.CurrentRow.DataBoundItem, DataRowView).EndEdit()
      End If
   End Sub

   Private Sub dt_RowChanged(ByVal sender As Object, ByVal e As System.Data.DataRowChangeEventArgs) Handles dt.RowChanged
      'write current values to Immediate Window
      Debug.WriteLine(String.Format("check1: {0}, check2: {1}, text1: {2}", e.Row(0).ToString, e.Row(1).ToString, e.Row(2).ToString))
   End Sub
End Class


Wednesday, March 28, 2012 1:44 PM

Hi Ryan,

How are you?

Would you like to try this workaround?

    Private dt As New DataTable
    Private Sub TestCheckboxForm_Load2(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
        dt.Columns.Add("index")
        dt.Columns.Add("yes/No")
        dt.Columns(0).DataType = GetType(Integer)
        dt.Columns(1).DataType = GetType(Boolean)
        dt.Rows.Add(1, True)
        dt.Rows.Add(2, True)
        dt.Rows.Add(3, True)
        'AddHandler dt.RowChanged, AddressOf DataTableRowChanged
        Me.DataGridView1.DataSource = dt
    End Sub

    Private Sub DataGridView1_CurrentCellDirtyStateChanged(sender As Object, e As System.EventArgs) Handles DataGridView1.CurrentCellDirtyStateChanged
        If Me.DataGridView1.CurrentCell.ColumnIndex = 1 Then
            Me.DataGridView1.EndEdit(DataGridViewDataErrorContexts.Commit)
            DataTableRowChanged(dt, New DataRowChangeEventArgs(dt.Rows(Me.DataGridView1.CurrentCell.RowIndex), DataRowAction.Change))
        End If
    End Sub

When the checkbox value is changed, I manually called the event handler.

Best regards,

Mike Feng
MSDN Community Support | Feedback to us
Please remember to mark the replies as answers if they help and unmark them if they provide no help.


Wednesday, March 28, 2012 1:59 PM

Hi Ryan,

When you call the endedit method, the changes are saved into the data table, you can check this will this test code:

    Private dt As New DataTable
    Private Sub TestCheckboxForm_Load2(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
        dt.Columns.Add("index")
        dt.Columns.Add("yes/No")
        dt.Columns(0).DataType = GetType(Integer)
        dt.Columns(1).DataType = GetType(Boolean)
        dt.Rows.Add(1, True)
        dt.Rows.Add(2, True)
        dt.Rows.Add(3, True)
        'AddHandler dt.RowChanged, AddressOf DataTableRowChanged
        AddHandler dt.ColumnChanged, AddressOf DataTableColChanged
        
        Me.DataGridView1.DataSource = dt
    End Sub

    Private Sub DataGridView1_CurrentCellDirtyStateChanged(sender As Object, e As System.EventArgs) Handles DataGridView1.CurrentCellDirtyStateChanged
        If Me.DataGridView1.CurrentCell.ColumnIndex = 1 Then
            Me.DataGridView1.EndEdit()
        End If
    End Sub

    Private Sub DataTableRowChanged(sender As Object, e As DataRowChangeEventArgs)
        Console.WriteLine(e.Action)
    End Sub

    Private Sub DataTableColChanged(sender As Object, e As DataColumnChangeEventArgs)
        Console.WriteLine(e.ProposedValue)
    End Sub

So would you like to change an event for your goal?

Best regards,

Mike Feng
MSDN Community Support | Feedback to us
Please remember to mark the replies as answers if they help and unmark them if they provide no help.


Wednesday, March 28, 2012 5:37 PM

Thanks Mike for the reply.  I don't believe the workaround is going to work for me.  Although your solution does work for a CheckBox Column, it will not work for a TextBox column in my situation.  I update my database whenever a data row is changed in the underlying DataTable_RowChanged event.

I disagree that the EndEdit() method pushes the new DataGridView cell value to the underlying DataTable.  If I get a fresh datatable and set it as the DataSource for DataGridView1 then left-click the first row of the DataGridView1 in Column1 (TextBox Column).  Type in "test" then right-click the column header.  Place this code in the DataGridView1_MouseDown event.  The returned DataTable (dt) has no rows in it.  This tells me the EndEdit() method is not working as it should or I'm doing something wrong.

Private Sub DataGridView1_MouseDown(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles DataGridView1.MouseDown

   ' run this block of code if right-clicked
   If e.Button = Windows.Forms.MouseButtons.Right Then

       ' ensure the datagridview data matches the underlying datatable before proceeding
       If CType(sender, DataGridView).IsCurrentRowDirty Then Me.DataGridView1.EndEdit()

   ' get a subset of the original datatable, where CheckBox column is True
   Dim dv As DataView = CType(Me.DataGridView1.DataSource, DataTable).AsDataView
   dv.RowFilter = "Column1 = 'test'"
   Dim dt As DataTable = dv.ToTable
   Me.DataGridView1.DataSource = dt

    ' call datagridview column context menu
    Me.ShowContextMenu(sender, e, Me.flpPending)

    End If

End Sub

Ryan


Friday, March 30, 2012 4:08 PM

Thanks TnTinMN!  

For some reason adding the line of code below fires the RowChanged event.  Seems strange to me you would have to EndEdit() for the DataGridView and the DataBoundItem.  I assumed EndEdit() would automatically push the data to the underlying table.  I even tried a BindingSource thinking it would push the data for me when dgv.EndEdit() was executed, but nope.  I had to EndEdit on the DataBoundItem as well.

CType(dgv.CurrentRow.DataBoundItem, DataRowView).EndEdit()

Ryan