Color ComboBox

JuggaloBrotha

VB.NET Forum Moderator
Staff member
Joined
Jun 3, 2004
Messages
4,530
Location
Lansing, MI; USA
Programming Experience
10+
I'm trying to turn this into a UserControl for a project and my end goal is to have a combobox that lists all of the colors in the color enum and it also shows a little box next to each item in the drop down that shows the color of each color's name. Just like the one in VS 2005 when you click Tools -> Options -> Fonts & Colors, there's an "Item Foreground" and "Item Background" that does this.

I already have the drawing of the colors and whatnot done, here's the code:
This fills the colors in the combo box itself
VB.NET:
        Dim CurrColor As Color
        For Each aColorName As String In System.Enum.GetNames(GetType(System.Drawing.KnownColor))
            CurrColor = Color.FromName(aColorName)
            If CurrColor.IsSystemColor = False Then NumerPanelColorComboBox.Items.Add(CurrColor)
        Next aColorName
This handles the DrawItem stuff:
VB.NET:
    Private Sub ComboBox_DrawItem(ByVal sender As Object, ByVal e As System.Windows.Forms.DrawItemEventArgs) Handles NumerPanelColorComboBox.DrawItem
        e.DrawBackground()
        e.DrawFocusRectangle()
        If e.Index >= 0 Then
            ' Get the Color object from the Items list
            Dim aColor As Color = CType(CType(sender, ComboBox).Items(e.Index), Color)

            ' get a square using the bounds height
            Dim rect As Rectangle = New Rectangle(2, e.Bounds.Top + 2, e.Bounds.Height, e.Bounds.Height - 5)
            Dim br As Brush = Brushes.White
            ' call these methods first
            e.DrawBackground()
            e.DrawFocusRectangle()
            ' change brush color if item is selected
            If e.State = DrawItemState.Selected OrElse e.State = DrawItemState.ComboBoxEdit Then
                br = Brushes.White
            Else
                br = Brushes.Black
            End If

            ' draw a rectangle and fill it
            e.Graphics.DrawRectangle(New Pen(aColor), rect)
            e.Graphics.FillRectangle(New SolidBrush(aColor), rect)

            ' draw a border
            rect.Inflate(1, 1)
            e.Graphics.DrawRectangle(Pens.Black, rect)

            ' draw the Color name
            e.Graphics.DrawString(aColor.Name, CType(sender, ComboBox).Font, br, e.Bounds.Height + 5, ((e.Bounds.Height - CType(sender, ComboBox).Font.Height) \ 2) + e.Bounds.Top)
        End If
    End Sub
This all works, however when you highlight the items in the combo box, the item's text remains black. I would like it if the item's text turns white (like how VS 2005 does) and when you move the mouse over another item, it turns the new item white and returns the old item back to black.

Any suggestions?
 
Try this:
VB.NET:
If (e.State And DrawItemState.Selected) = DrawItemState.Selected Then
DrawItemState is a flags enumeration, and e.State may contain a combination of multiple of these values.
 
Thanks John, I didn't even think of that.

I'm going to see about putting this into a custom control and post the source here.
 
Here's something you can use to sort similar colors like the VS web color list:
VB.NET:
Private Function SortColors(ByVal x As Color, ByVal y As Color) As Integer
    Dim huecompare As Integer = x.GetHue.CompareTo(y.GetHue)
    Dim satcompare As Integer = x.GetSaturation.CompareTo(y.GetSaturation)
    Dim brightcompare As Integer = x.GetBrightness.CompareTo(y.GetBrightness)
    If huecompare <> 0 Then
        Return huecompare        
    ElseIf satcompare <> 0 Then
        Return satcompare
    ElseIf brightcompare <> 0 Then
        Return brightcompare
    Else
        Return 0
    End If
End Function
To use it first add all non-system colors to a List(Of Color), sort it by calling colors.Sort(AddressOf SortColors), then add all the list colors to the combo Items.
 
Good thinking, right now I'm having trouble over riding the Base's DrawMode and DropDownStyle, I want these properties to remain fixed both in design time and run time. Here's what I have so far and the error message I keep getting is a null reference but it doesn't give me a line number or anything, it happens when the form's first being built.
VB.NET:
Option Explicit On
Option Strict On

Friend Class ColorComboBox
    Inherits ComboBox

    Private _ColorList As List(Of Color)

    Friend Sub New()
        _ColorList = New List(Of Color)
        Me.DrawMode = Windows.Forms.DrawMode.OwnerDrawFixed
        Me.DropDownStyle = ComboBoxStyle.DropDownList
        Dim CurrColor As Color
        For Each aColorName As String In System.Enum.GetNames(GetType(System.Drawing.KnownColor))
            CurrColor = Color.FromName(aColorName)
            If CurrColor.IsSystemColor = False AndAlso CurrColor <> Color.Transparent Then
                _ColorList.Add(CurrColor)
            End If
        Next aColorName
        _ColorList.Sort(AddressOf SortColors)
        For Counter As Integer = 0I To _ColorList.Count - 1I
            Me.Items.Add(_ColorList(Counter))
        Next Counter
    End Sub

    Protected Overrides Sub onDrawItem(ByVal e As System.Windows.Forms.DrawItemEventArgs)
        e.DrawBackground()
        e.DrawFocusRectangle()
        If e.Index >= 0 Then
            ' Get the Color object from the Items list
            Dim aColor As Color = CType(Me.Items(e.Index), Color)

            ' get a square using the bounds height
            Dim rect As Rectangle = New Rectangle(2I, e.Bounds.Top + 2I, e.Bounds.Height - 3I, e.Bounds.Height - 5I)
            Dim br As Brush = Brushes.White
            ' change brush color if item is selected
            If (e.State And DrawItemState.Selected) = DrawItemState.Selected Then
                br = Brushes.White
            Else
                br = Brushes.Black
            End If

            ' draw a rectangle and fill it
            e.Graphics.DrawRectangle(New Pen(aColor), rect)
            e.Graphics.FillRectangle(New SolidBrush(aColor), rect)

            ' draw a border
            rect.Inflate(1I, 1I)
            e.Graphics.DrawRectangle(Pens.Black, rect)

            ' draw the Color name
            e.Graphics.DrawString(aColor.Name, Me.Font, br, e.Bounds.Height + 5I, ((e.Bounds.Height - Me.Font.Height) \ 2I) + e.Bounds.Top)
        End If
    End Sub

    Private Function SortColors(ByVal x As Color, ByVal y As Color) As Integer
        Dim huecompare As Integer = x.GetHue.CompareTo(y.GetHue)
        Dim satcompare As Integer = x.GetSaturation.CompareTo(y.GetSaturation)
        Dim brightcompare As Integer = x.GetBrightness.CompareTo(y.GetBrightness)
        If huecompare <> 0I Then
            Return huecompare
        ElseIf satcompare <> 0I Then
            Return satcompare
        ElseIf brightcompare <> 0I Then
            Return brightcompare
        Else
            Return 0I
        End If
    End Function

    Friend Shadows Property DrawMode() As DrawMode
        Get
            Return Windows.Forms.DrawMode.OwnerDrawFixed
        End Get
        Set(ByVal value As DrawMode)
            MyBase.DrawMode = Windows.Forms.DrawMode.OwnerDrawFixed
        End Set
    End Property


    Friend Shadows Property DropDownStyle() As ComboBoxStyle
        Get
            Return ComboBoxStyle.DropDownList
        End Get
        Set(ByVal value As ComboBoxStyle)
            MyBase.DropDownStyle = ComboBoxStyle.DropDownList
        End Set
    End Property
End Class
Could you help me with this one?
 
I think it would hide it like this: (Imports System.ComponentModel)
VB.NET:
<Browsable(False), _
EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never), _
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)> _
Public Shadows ReadOnly Property DrawMode() As DrawMode
    Get
        Return Windows.Forms.DrawMode.OwnerDrawFixed
    End Get
End Property
When the property value can't be changed you don't need the setter, and you don't need the property to be visible because it doesn't return usable information for the consumer. Internally in class you can refer to MyBase.DrawMode instead of Me.DrawMode.
 
Back
Top