vb.netdatagridviewdatagridviewcomboboxcellcell-formatting

DataGridView CellFormatting event firing repeatedly while trying to link ComboBox column


I have a DataGridView populated by a DataTable. The view is in a form which is to be used to allow a user to edit the data, add new rows etc. One of the columns in the set, I want to be selectable from a list via a ComboBox. Because there is pre-existing data loaded at the start (the "current state"), I have to add a separate DataGridViewComboBoxColumn and then "set" the values for the pre-existing data from an identifier in a separate (hidden) column. I set the DisplayType of the combobox column to Nothing so that they appear like flat textboxes.

Private featureIDColumn As DataGridViewColumn
Private cboFeatureColumn As DataGridViewComboBoxColumn

With dgv
    .DataSource = dtbMyDataTable
    featureIDColumn = .Columns("FeatureID")
    featureIDColumn.Visible = False
    cboFeatureColumn = New DataGridViewComboBoxColumn() With {.DisplayIndex = featureIDColumn.Index + 1}
    With cboFeatureColumn
        .Name = "Feature"
        .DataSource = dtbFeatureList    ' Simple two-column lookup of ID's and values
        .ValueMember = "ID"
        .DisplayMember = "FriendlyName"
        .HeaderText = "Feature"
        .DisplayStyle = DataGridViewComboBoxDisplayStyle.Nothing
    End With
    .Columns.Add(cboFeatureColumn)
    For Each row As DataGridViewRow In dgv.Rows
        Dim cboCell As DataGridViewComboBoxCell = CType(row.Cells(cboFeatureColumn.Index), DataGridViewComboBoxCell)
        cboCell.Value = row.Cells(featureIDColumn.Index).Value
    Next
End With

I've added a (derived) button column to the grid with the intention that when the user clicks it, a new record is inserted and the DisplayType of the DataGridViewComboBoxCell on that specific row changes to ComboBox (so they can select the value from the dropdown) but all the other cells in the column retain the appearance of a flat textbox.

Private Sub copyAction(clickedCell As DataGridViewCustomButtonCell)
    Try
        Dim activeRow As DataGridViewRow = dgv.Rows(clickedCell.RowIndex)
        Dim newRow As DataRow = dtbMyDataTable.NewRow
        For Each dtc As DataColumn In dtbMyDataTable.Columns
            Select Case dtc.ColumnName
                Case "FeatureID"
                    newRow.Item(dtc.ColumnName) = 0
                Case "Status"
                    newRow.Item(dtc.ColumnName) = "New"
                Case Else
                    ' Various default values are set for certain columns
            End Select
        Next
        dtbActive.Rows.Add(newRow)
        dtbActive.AcceptChanges()

    Catch ex As Exception

    End Try
End Sub

Private Sub dgv_CellFormatting(sender As Object, e As DataGridViewCellFormattingEventArgs) Handles dgv.CellFormatting
    Try
        If Not e.RowIndex < 0 Then
            Dim activeRow As DataGridViewRow = dgv.Rows(e.RowIndex)
            For Each cll As DataGridViewCell In activeRow.Cells
                If activeRow.Cells(statusColumn.Index).Value = "New" Then
                    If cll.ColumnIndex = cboFeatureColumn.Index Then
                        Dim cbo As DataGridViewComboBoxCell = CType(dgv.Rows(cll.RowIndex).Cells(cll.ColumnIndex), DataGridViewComboBoxCell)
                        cbo.DisplayStyle = DataGridViewComboBoxDisplayStyle.ComboBox
                        cll.Style.BackColor = Color.White
                        cll.Style.ForeColor = Color.DarkBlue
                        cll.ReadOnly = False
                    Else
                        cll.Style.BackColor = Color.LightGreen
                        cll.Style.ForeColor = Color.DarkGreen
                        cll.ReadOnly = True
                    End If
                End If
            Next
        End If

    Catch ex As Exception

    End Try
End Sub

When they select an item from the dropdown, the associated value (identifier) is updated in the ID field (this is the "true" record; the ComboBox is merely there to serve as an easy means of selecting from a specific set of values)

Private Sub dgv_CellValueChanged(sender As Object, e As DataGridViewCellEventArgs) Handles dgv.CellValueChanged
    Try
        Dim changedCell As DataGridViewCell = CType(dgv.Rows(e.RowIndex).Cells(e.ColumnIndex), DataGridViewCell)
        If changedCell.ColumnIndex = cboFeatureColumn.Index Then
            Dim changedCombo = CType(changedCell, DataGridViewComboBoxCell)
            dgv.Rows(changedCell.RowIndex).Cells(featureIDColumn.Index).Value = changedCombo.Value
        End If
    Catch ex As Exception

    End Try
End Sub

But - as soon as the new record is created, all the values in the combobox column for the pre-existing records, disappear? And that, I am assuming, is because the combobox column only exists in the DataGridView, not in the underlying DataTable (?) So the DataGridView refreshes from its DataSource and blanks that column.

Okay - so to get around that problem, I created a Sub that refreshes that column from the ID column which I can call whenever the data changes :

Private Sub RefreshFeatureColumn()
    Try
        For Each row As DataGridViewRow In dgv.Rows
            Dim cboCell As DataGridViewComboBoxCell = CType(row.Cells(cboFeatureColumn.Index), DataGridViewComboBoxCell)
            cboCell.Value = row.Cells(featureIDColumn.Index).Value
        Next
    Catch ex As Exception

    End Try
End Sub

But now, whenever I click the button, it appears to me that the CellFormatting (and/or possibly the CellPainting ?) event is just firing constantly, rendering the form unusable (it's just flickering perpetually and not capturing any clicks - effectively, hanging)

So - I'm obviously doing something badly wrong, but how else can I use a ComboBox column to manipulate the value in the ID column? Is there any way of handling the CellFormatting events such that it only fires when I want/need it to? Or, perhaps more appropriately, is there a way I can achieve what I'm trying to achieve in a way that doesn't fire the CellFormatting event perpetually like this?

Thanks in advance and apologies for the length of the question, I stripped the code down as much as I possibly could but wanted to include as much as possible to fully explain what it is I'm trying to do (and I can't imagine I'm the first?) I'm sure there's a far more elegant way of doing this but I haven't been able to figure it out.


Solution

  • you dont need cellformatting event you can do your code on the mouse click or mouse double click event on your datagridview all you have to do is to remove the event from the cellformatting event against your datagrid and create an event on mouse click or mouse double click and try to write your code from there

    try that and let me what happened