Color ComboBox

JuggaloBrotha

VB.NET Forum Moderator
Staff member
Joined
Jun 3, 2004
Messages
4,530
Location
Lansing, MI; USA
Programming Experience
10+
Here's a custom control that automatically handles displaying colors in a ComboBox. My goal is to have it be flexible (Automatically include system colors, or an array of user selected colors) and I'd like other people to test it out.
VB.NET:
Option Explicit On
Option Strict On

Imports System.ComponentModel

Public Class ColorComboBox
    Inherits ComboBox

    Private _ColorList As List(Of Color)
    Private _SortAlphabetical As Boolean
    Private _IncludeTransparent As Boolean
    Private _IncludeSystemColors As Boolean
    Private _MinimumSize As New Size(60I, 21I)

#Region " Constructors "
    Public Sub New()
        Call PreInitialize()
        Call Initialize()
    End Sub

    Public Sub New(ByVal IncludeTransparent As Boolean, ByVal IncludeSystemColors As Boolean, ByVal Alphabetical As Boolean)
        _IncludeTransparent = IncludeTransparent
        _IncludeSystemColors = IncludeSystemColors
        _SortAlphabetical = Alphabetical
        Call PreInitialize()
        Call Initialize()
    End Sub

    Public Sub New(ByVal TheColors() As Color)
        Call PreInitialize()
        _ColorList.AddRange(TheColors)
        Call SetValues()
    End Sub
#End Region
#Region " Properties "
    <Description("Sort the colors in the DropDown alphabetically"), DefaultValue(False), Category("Colors")> _
    Public Property SortAlphabetically() As Boolean
        Get
            Return _SortAlphabetical
        End Get
        Set(ByVal value As Boolean)
            If _SortAlphabetical <> value Then
                _SortAlphabetical = value
                Call SetValues()
            End If
        End Set
    End Property

    <Description("Include the transparent color in the DropDown"), DefaultValue(False), Category("Colors")> _
    Public Property IncludeTransparent() As Boolean
        Get
            Return _IncludeTransparent
        End Get
        Set(ByVal value As Boolean)
            If _IncludeTransparent <> value Then
                _IncludeTransparent = value
                If value = True Then
                    If _ColorList.Contains(Color.Transparent) = False Then
                        _ColorList.Add(Color.Transparent)
                        Call SetValues()
                    End If
                Else
                    If _ColorList.Contains(Color.Transparent) = True Then
                        _ColorList.Remove(Color.Transparent)
                        Call SetValues()
                    End If
                End If
            End If
        End Set
    End Property

    <Description("Include the system colors in the DropDown"), DefaultValue(False), Category("Colors")> _
    Public Property IncludeSystemColors() As Boolean
        Get
            Return _IncludeSystemColors
        End Get
        Set(ByVal value As Boolean)
            If _IncludeSystemColors <> value Then
                _IncludeSystemColors = value
                If value = True Then
                    Call AddSystemColors()
                Else
                    Call RemoveSystemColors()
                End If
            End If
        End Set
    End Property

    <Browsable(False), EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), _
    DefaultValue(False)> _
    Public Shadows ReadOnly Property Sorted() As Boolean
        Get
            Return False
        End Get
    End Property

    <Browsable(False), EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), _
    DefaultValue(Windows.Forms.DrawMode.OwnerDrawFixed)> _
    Public Shadows ReadOnly Property DrawMode() As System.Windows.Forms.DrawMode
        Get
            Return System.Windows.Forms.DrawMode.OwnerDrawFixed
        End Get
    End Property

    <Browsable(False), EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), _
    DefaultValue(System.Windows.Forms.ComboBoxStyle.DropDownList)> _
    Public Shadows ReadOnly Property DropDownStyle() As System.Windows.Forms.ComboBoxStyle
        Get
            Return System.Windows.Forms.ComboBoxStyle.DropDownList
        End Get
    End Property

    <Browsable(False), EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), _
    DefaultValue("60, 21")> _
    Public Shadows ReadOnly Property MinimumSize() As System.Drawing.Size
        Get
            Return _MinimumSize
        End Get
    End Property
#End Region
#Region " Methods "
    <Description("Add a color to the DropDown")> _
    Public Sub Add(ByVal Item As Color)
        If _ColorList.Contains(Item) = False Then
            _ColorList.Add(Item)
            Call SetValues()
        End If
    End Sub

    <Description("Add an array of colors to the DropDown")> _
    Public Sub AddRange(ByVal Items() As Color)
        For Each clr As Color In Items
            If _ColorList.Contains(clr) = False Then _ColorList.Add(clr)
        Next clr
        Call SetValues()
    End Sub

    <Description("Removes a color from the DropDown")> _
    Public Sub Remove(ByVal Item As Color)
        If _ColorList.Contains(Item) = False Then
            _ColorList.Remove(Item)
            Call SetValues()
        End If
    End Sub

    Public Sub RemoveRange(ByVal Items() As Color)
        For Each clr As Color In Items
            If _ColorList.Contains(clr) = False Then _ColorList.Remove(clr)
        Next clr
        Call SetValues()
    End Sub
#End Region
#Region " Initialize, SetValues "
    Private Sub PreInitialize()
        MyBase.DrawMode = Windows.Forms.DrawMode.OwnerDrawFixed
        MyBase.DropDownStyle = ComboBoxStyle.DropDownList
        MyBase.Sorted = False
        MyBase.MinimumSize = _MinimumSize
        _ColorList = New List(Of Color)
    End Sub

    Private Sub Initialize()
        If DesignMode = False Then
            Dim CurrColor As Color
            _ColorList.Clear()
            For Each aColorName As String In System.Enum.GetNames(GetType(System.Drawing.KnownColor))
                CurrColor = Color.FromName(aColorName)
                If _IncludeSystemColors = True Then
                    If _IncludeTransparent = True Then
                        If _ColorList.Contains(CurrColor) = False Then _ColorList.Add(CurrColor)
                    Else
                        If CurrColor <> Color.Transparent AndAlso _ColorList.Contains(CurrColor) = False Then _ColorList.Add(CurrColor)
                    End If
                Else
                    If _IncludeTransparent = True Then
                        If CurrColor.IsSystemColor = False AndAlso _ColorList.Contains(CurrColor) = False Then _ColorList.Add(CurrColor)
                    Else
                        If CurrColor.IsSystemColor = False AndAlso CurrColor <> Color.Transparent AndAlso _ColorList.Contains(CurrColor) = False Then _ColorList.Add(CurrColor)
                    End If
                End If
            Next aColorName
            Call SetValues()
        End If
    End Sub

    Private Sub SetValues()
        Me.Items.Clear()
        If DesignMode = False Then
            If _SortAlphabetical = False Then
                _ColorList.Sort(AddressOf SortColors)
            Else
                _ColorList.Sort(AddressOf SortAlphabetical)
            End If
            For Counter As Integer = 0I To _ColorList.Count - 1I
                MyBase.Items.Add(_ColorList(Counter))
            Next Counter
        End If
    End Sub
#End Region
#Region " AddSystemColors, RemoveSystemColors "
    Private Sub AddSystemColors()
        If DesignMode = False Then
            Dim CurrColor As Color
            For Each aColorName As String In System.Enum.GetNames(GetType(System.Drawing.KnownColor))
                CurrColor = Color.FromName(aColorName)
                If CurrColor.IsSystemColor = True AndAlso _ColorList.Contains(CurrColor) = False Then _ColorList.Add(CurrColor)
            Next aColorName
            Call SetValues()
        End If
    End Sub

    Private Sub RemoveSystemColors()
        If DesignMode = False Then
            For Counter As Integer = _ColorList.Count - 1I To 0I Step -1I
                If _ColorList(Counter).IsSystemColor = True Then _ColorList.RemoveAt(Counter)
            Next Counter
            'For Each clr As Color In _ColorList
            '    If clr.IsSystemColor = True Then _ColorList.Remove(clr)
            'Next clr
            Call SetValues()
        End If
    End Sub
#End Region
#Region " Overrides Sub "
    Protected Overrides Sub OnFontChanged(ByVal e As System.EventArgs)
        Select Case True
            Case Me.Font.SizeInPoints > 12.0F : Me.Font = New Font(Me.Font.FontFamily, 12.0F, Me.Font.Style)
            Case Me.Font.SizeInPoints < 8.0F : Me.Font = New Font(Me.Font.FontFamily, 8.0F, Me.Font.Style)
        End Select
        'If Me.Font.SizeInPoints > 12.0F Then Me.Font = New Font(Me.Font.FontFamily, 12.0F, Me.Font.Style)
    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(5I, 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
#End Region
#Region " SortColors "
    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)
        Select Case True
            Case huecompare <> 0I : Return huecompare
            Case satcompare <> 0I : Return satcompare
            Case brightcompare <> 0I : Return brightcompare
            Case Else : Return 0I
        End Select
    End Function

    Private Function SortAlphabetical(ByVal x As Color, ByVal y As Color) As Integer
        Return x.Name.CompareTo(y.Name)
    End Function
#End Region

End Class
Here's a reference thread on where this thing started: http://www.vbdotnetforums.com/component-development/28782-color-combobox.html
 
Known Issues:
-Using it in the designer, the IDE causes incorrect code to be generated and will cause the control to not be set to an instance of an object. I'm looking for advice on resolving this.
"incorrect code"? If you mean DrawMode and DropDownStyle is generated when you tried to hide them, then this is caused by setting Friend access level when shadowing a Public property, change them to Public. "not instance"? Perhaps this happened when you had an instance in form and changed the access level to Friend, which caused something to go wrong in generated code. To fix this you have to start a new form (or edit the generated code of current to remove the left over garbage). Also change the parameterless constructor access level from Friend to Public, doesn't seem to play well here. Friend should be equal to Public within the assembly, but apparently they're not in IDE.

Another tip: add a property where user can choose to sort colors alphabetical or by HSL.
 
I never knew that, and by changing the stuff from Friend back to Public, the whole object not set to an instance thing has also been resolved. I've updated the original post with the new code.
 
Back
Top