Search code examples
vb.netdatagridviewdatagridviewcombobox

Set color of specific item within a DataGridViewComboBox dropdown list


In the following example, how can I highlight (change the background color) of specific items in the dropdown list? To be specific, I am talking about the items in the "dropdown list" for the DataGridViewComboBox.

I want to highlight for the user the specific item(s) that are bad choices.

Please provide a working code example in vb.net.

Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    Dim dt As New DataTable

    dt.Columns.Add("id", GetType(Integer))
    dt.Columns.Add("list", GetType(String))
    dt.Columns.Add("goodChoice", GetType(Boolean))

    dt.Rows.Add(10, "Lemon")
    dt.Rows.Add(20, "Apple")
    dt.Rows.Add(30, "Star Fruit", False)
    dt.Rows.Add(40, "Orange")

    Dim newColumn As New DataGridViewComboBoxColumn()
    With newColumn
        .HeaderText = "Choices"
        .Name = "Choices"
        .DataSource = dt
        .DisplayMember = "list"
        .ValueMember = "id"
    End With
    DataGridView1.Columns.Add(newColumn)
End Sub

Solution

  • Try handling the EditingControlShowing event to subscribe to the ComboBox.DrawItem event. In this event handler you'll grab the underlying DataTable DataSource. When that item's goodChoice is False and the item is not the focused item, fill it's background color.

    Private Sub DataGridView1_EditingControlShowing(sender As Object, e As DataGridViewEditingControlShowingEventArgs) Handles DataGridView1.EditingControlShowing
        If TypeOf e.Control Is ComboBox Then
            Dim cb As ComboBox = TryCast(e.Control, ComboBox)
            cb.DrawMode = DrawMode.OwnerDrawFixed
            RemoveHandler cb.DrawItem, AddressOf DrawGridComboBoxItem
            AddHandler cb.DrawItem, AddressOf DrawGridComboBoxItem
        End If
    End Sub
    
    Private Sub DrawGridComboBoxItem(sender As Object, e As DrawItemEventArgs)
        If e.Index <> -1 Then
    
            e.DrawBackground()
    
            Dim cb As ComboBox = TryCast(sender, ComboBox)
            Dim dt As DataTable = TryCast(cb.DataSource, DataTable)
    
            If (e.State And DrawItemState.Focus) <> DrawItemState.Focus AndAlso cb.DroppedDown Then
                If dt.Rows(e.Index).Item("goodChoice") Then
                    e.Graphics.FillRectangle(Brushes.White, e.Bounds)
                Else ' Added for follow-up question.
                    e.Graphics.FillRectangle(Brushes.Red, e.Bounds)
                End If
            End If
    
            e.Graphics.DrawString(dt.Rows(e.Index).Item("Name"), e.Font, Brushes.Black, e.Bounds)
    
            e.DrawFocusRectangle()
        End If
    End Sub
    

    My follow-up question is, if and when the red colored item is selected, how can I make the color stick when exiting the edit mode of the control?

    Set the BackColor of the cell itself. (Note: This will also alter the dropped down items, so we add an Else statement to the DrawGridComboBoxItem method above.)

    Set the color when the cell value changes:

    Private Sub DataGridView1_ValueChanged(sender As Object, e As DataGridViewCellEventArgs) Handles DataGridView1.CellValueChanged
        If TypeOf DataGridView1.CurrentCell Is DataGridViewComboBoxCell Then
            Dim cell As DataGridViewComboBoxCell = TryCast(DataGridView1.Rows(e.RowIndex).Cells(e.ColumnIndex), DataGridViewComboBoxCell)
            Dim dt As DataTable = TryCast(cell.DataSource, DataTable)
            Dim row() As DataRow = dt.Select("ID = " & cell.Value)
    
            cell.Style.BackColor = If(row(0).Item("goodChoice"), SystemColors.ControlLight, Color.Red)
        End If
    End Sub
    

    Then use that color by manually painting the background:

    Private Sub DataGridView1_CellPainting(sender As Object, e As DataGridViewCellPaintingEventArgs) Handles DataGridView1.CellPainting
        If e.RowIndex >= 0 And e.ColumnIndex >= 0 Then
            If TypeOf DataGridView1.Rows(e.RowIndex).Cells(e.ColumnIndex) Is DataGridViewComboBoxCell Then
                Dim cell As DataGridViewComboBoxCell = DataGridView1.Rows(e.RowIndex).Cells(e.ColumnIndex)
                Dim color As Color = If(cell.Style.ForeColor.Name = "0", Color.Black, cell.Style.ForeColor)
    
                Using backbrush = New SolidBrush(cell.Style.BackColor)
                    Using brush = New SolidBrush(color)
                        Using format = New StringFormat()
                            e.Paint(e.ClipBounds, DataGridViewPaintParts.Background)
                            e.Paint(e.ClipBounds, DataGridViewPaintParts.Border)
                            e.Paint(e.ClipBounds, DataGridViewPaintParts.ContentBackground)
                            e.Paint(e.ClipBounds, DataGridViewPaintParts.ErrorIcon)
                            e.Paint(e.ClipBounds, DataGridViewPaintParts.Focus)
                            e.Paint(e.ClipBounds, DataGridViewPaintParts.SelectionBackground)
    
                            format.LineAlignment = StringAlignment.Center
    
                            Dim rect = New Rectangle(e.CellBounds.X + 1, e.CellBounds.Y + 1, e.CellBounds.Width - 19, e.CellBounds.Height - 3)
    
                            e.Graphics.FillRectangle(backbrush, rect)
                            e.Graphics.DrawString(cell.FormattedValue, e.CellStyle.Font, brush, e.CellBounds, format)
                            e.Handled = True
                        End Using
                    End Using
                End Using
            End If
        End If
    End Sub