User1
User1

Reputation: 3

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

Upvotes: 0

Views: 2061

Answers (1)

OhBeWise
OhBeWise

Reputation: 5454

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

Upvotes: 2

Related Questions