﻿Imports System.ComponentModel

Public Class ColorListBox
    Inherits ListBox

    Public Enum HotSpots As Integer
        ITEM_IMAGE = 0
        ICON1 = 1
        ICON2 = 2
        ICON3 = 3
        DoubleClick = -1
    End Enum

    Public Event ClickedHotSpot(ByVal Index As Integer, ByVal Spot As HotSpots, ByVal Tag As String)

    Public Shadows Sub ItemClickedSpot(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles Me.MouseDown

        If e.Button = MouseButtons.Left Then
            If e.Clicks = 1 Then
                Select Case (e.Y - (Me.IndexFromPoint(e.X, e.Y) * Me.ItemHeight))
                    Case 22 To 63
                        Select Case e.X
                            Case 94 To 126
                                RaiseEvent ClickedHotSpot(Me.IndexFromPoint(e.X, e.Y), HotSpots.ICON1, Me.Items.Item(Me.IndexFromPoint(e.X, e.Y)).Tag)
                            Case 234 To 266
                                RaiseEvent ClickedHotSpot(Me.IndexFromPoint(e.X, e.Y), HotSpots.ICON2, Me.Items.Item(Me.IndexFromPoint(e.X, e.Y)).Tag)
                            Case 374 To 406
                                RaiseEvent ClickedHotSpot(Me.IndexFromPoint(e.X, e.Y), HotSpots.ICON3, Me.Items.Item(Me.IndexFromPoint(e.X, e.Y)).Tag)
                        End Select
                End Select

                Select Case (e.Y - (Me.IndexFromPoint(e.X, e.Y) * Me.ItemHeight))
                    Case 10 To 58
                        Select Case e.X
                            Case 14 To 56
                                RaiseEvent ClickedHotSpot(Me.IndexFromPoint(e.X, e.Y), HotSpots.ITEM_IMAGE, Me.Items.Item(Me.IndexFromPoint(e.X, e.Y)).Tag)
                        End Select
                End Select

            End If
            If e.Clicks = 2 Then
                RaiseEvent ClickedHotSpot(Me.IndexFromPoint(e.X, e.Y), HotSpots.DoubleClick, Me.Items.Item(Me.IndexFromPoint(e.X, e.Y)).Tag)
            End If
        End If
    End Sub

    Public Sub New()
        Me.DrawMode = Windows.Forms.DrawMode.OwnerDrawFixed
        _Items = New ColorListBoxItemCollection(Me)
        _ShowImages = True
        _TextAlign = ContentAlignment.MiddleLeft
    End Sub

    Private _Items As ColorListBoxItemCollection
    <DesignerSerializationVisibility(DesignerSerializationVisibility.Content)> _
    Public Overloads ReadOnly Property Items() As ColorListBoxItemCollection
        Get
            Return _Items
        End Get
    End Property

    'The original items that the user will never see.
    Private ReadOnly Property baseItems() As ObjectCollection
        Get
            Return MyBase.Items
        End Get
    End Property

    Public Overloads Property SelectedItem() As ColorListBoxItem
        Get
            Return DirectCast(MyBase.SelectedItem, ColorListBoxItem)
        End Get
        Set(ByVal value As ColorListBoxItem)
            MyBase.SelectedItem = value
        End Set
    End Property

    Public Overloads ReadOnly Property SelectedItems() As ColorListBoxSelectedItemCollection
        Get
            Dim items As New ColorListBoxSelectedItemCollection()
            For Each item As Object In MyBase.SelectedItems
                items.Add(DirectCast(item, ColorListBoxItem))
            Next
            Return items
        End Get
    End Property

    Private _ShowImages As Boolean
    <DefaultValue(True)> _
    Public Property ShowImages() As Boolean
        Get
            Return _ShowImages
        End Get
        Set(ByVal value As Boolean)
            If _ShowImages <> value Then
                _ShowImages = value
                Me.Invalidate()
            End If
        End Set
    End Property

    Private _TextAlign As ContentAlignment
    <DefaultValue(ContentAlignment.MiddleLeft)> _
    Public Property TextAlign() As ContentAlignment
        Get
            Return _TextAlign
        End Get
        Set(ByVal value As ContentAlignment)
            If _TextAlign <> value Then
                _TextAlign = value
                Me.Invalidate()
            End If
        End Set
    End Property

    Protected Overrides Sub OnDrawItem(ByVal e As System.Windows.Forms.DrawItemEventArgs)
        MyBase.OnDrawItem(e)
        'Draw original background and selection.
        'You can remove this and draw your own background if you want.
        'e.DrawBackground()
        e.Graphics.PixelOffsetMode = Drawing2D.PixelOffsetMode.HighQuality

        If (e.State And DrawItemState.Selected) = DrawItemState.Selected Then
            'If menu selected, we draw a yellowgreen highlighting
            'e.Graphics.FillRectangle(Brushes.LightCyan, e.Bounds)
            Dim p As New Pen(Color.LightGreen, 2.0F)
            e.Graphics.DrawLine(p, e.Bounds.X, e.Bounds.Y + Me.ItemHeight, e.Bounds.Width, e.Bounds.Y + Me.ItemHeight)
            e.Graphics.DrawImage(My.Resources.down, e.Bounds)
        Else
            'Not selected, we draw this light blue.
            'e.Graphics.FillRectangle(Brushes.GhostWhite, e.Bounds)
            Dim p As New Pen(Color.GhostWhite, 2.0F)
            e.Graphics.DrawLine(p, e.Bounds.X, e.Bounds.Y + Me.ItemHeight, e.Bounds.Width, e.Bounds.Y + Me.ItemHeight)
            e.Graphics.DrawImage(My.Resources.up, e.Bounds)
        End If

        'e.DrawFocusRectangle()

        If e.Index >= 0 AndAlso e.Index < Me.Items.Count Then
            Dim item As ColorListBoxItem = Me.Items(e.Index)

            If item IsNot Nothing Then
                e.Graphics.SmoothingMode = Drawing2D.SmoothingMode.HighQuality
                If Me.ShowImages _
                AndAlso item.Image IsNot Nothing Then
                    'Draw the image
                    e.Graphics.DrawImage(item.Image, _
                                         CInt(e.Bounds.X + item.Image.Width / 2 - 10), _
                                         e.Bounds.Y + 10, _
                                         item.Image.Width, _
                                         item.Image.Height)
                End If

                e.Graphics.SmoothingMode = Drawing2D.SmoothingMode.HighQuality
                If item.Icon1 IsNot Nothing Then
                    e.Graphics.DrawImage(item.Icon1, _
                              CInt(e.Bounds.X + item.Icon1.Width / 2 + Me.ItemHeight * 1), _
                              CInt(e.Bounds.Y + Me.ItemHeight / 3), _
                              CInt(item.Icon1.Width * 2 / 3), _
                              CInt(item.Icon1.Height * 2 / 3))

                    If item.Icon1_Label IsNot Nothing Then
                        e.Graphics.DrawString(item.Icon1_Label, item.Icons_Font, New SolidBrush(item.Icons_Forecolor), CInt(e.Bounds.X + item.Icon1.Width / 3 + Me.ItemHeight * 1), CInt(e.Bounds.Y + item.Icon1.Width + 4))
                    End If
                End If
                If item.Icon2 IsNot Nothing Then
                    e.Graphics.DrawImage(item.Icon2, _
                              CInt(e.Bounds.X + item.Icon2.Width / 2 + Me.ItemHeight * 3), _
                              CInt(e.Bounds.Y + Me.ItemHeight / 3), _
                              CInt(item.Icon2.Width * 2 / 3), _
                              CInt(item.Icon2.Height * 2 / 3))
                    If item.Icon2_Label IsNot Nothing Then

                        e.Graphics.DrawString(item.Icon2_Label, item.Icons_Font, New SolidBrush(item.Icons_Forecolor), CInt(e.Bounds.X + item.Icon2.Width / 3 + Me.ItemHeight * 3), CInt(e.Bounds.Y + item.Icon2.Width + 4))
                    End If
                End If
                If item.Icon3 IsNot Nothing Then
                    e.Graphics.DrawImage(item.Icon3, _
                              CInt(e.Bounds.X + item.Icon3.Width / 2 + Me.ItemHeight * 5), _
                              CInt(e.Bounds.Y + Me.ItemHeight / 3), _
                              CInt(item.Icon3.Width * 2 / 3), _
                              CInt(item.Icon3.Height * 2 / 3))
                    If item.Icon3_Label IsNot Nothing Then
                        e.Graphics.DrawString(item.Icon3_Label, item.Icons_Font, New SolidBrush(item.Icons_Forecolor), CInt(e.Bounds.X + item.Icon3.Width / 3 + Me.ItemHeight * 5), CInt(e.Bounds.Y + item.Icon3.Width + 4))
                    End If
                End If

            End If
            'Draw the item text
            DrawItemText(e, item)
        End If
    End Sub

    Private Sub DrawItemText(ByVal e As System.Windows.Forms.DrawItemEventArgs, ByVal item As ColorListBoxItem)
        Dim x, y As Single
        Dim textSize As SizeF = e.Graphics.MeasureString(item.Text, Me.Font)
        Dim w As Single = textSize.Width
        Dim h As Single = textSize.Height
        Dim bounds As Rectangle = e.Bounds

        'If we are showing images, make some room for them and adjust the bounds width.
        If Me.ShowImages Then
            bounds.X += Me.ItemHeight
            bounds.Width -= Me.ItemHeight
        End If

        'Depending on which TextAlign is chosen, determine the x and y position of the text.
        Select Case Me.TextAlign
            Case ContentAlignment.BottomCenter
                x = bounds.X + (bounds.Width - w) / 2
                y = bounds.Y + bounds.Height - h
            Case ContentAlignment.BottomLeft
                x = bounds.X
                y = bounds.Y + bounds.Height - h
            Case ContentAlignment.BottomRight
                x = bounds.X + bounds.Width - w
                y = bounds.Y + bounds.Height - h
            Case ContentAlignment.MiddleCenter
                x = bounds.X + (bounds.Width - w) / 2
                y = bounds.Y + (bounds.Height - h) / 2
            Case ContentAlignment.MiddleLeft
                x = bounds.X
                y = bounds.Y + (bounds.Height - h) / 2
            Case ContentAlignment.MiddleRight
                x = bounds.X + bounds.Width - w
                y = bounds.Y + (bounds.Height - h) / 2
            Case ContentAlignment.TopCenter
                x = bounds.X + (bounds.Width - w) / 2
                y = bounds.Y
            Case ContentAlignment.TopLeft
                x = bounds.X
                y = bounds.Y
            Case ContentAlignment.TopRight
                x = bounds.X + bounds.Width - w
                y = bounds.Y
        End Select

        'Finally draw the text.
        e.Graphics.DrawString(" " & item.Text, Me.Font, New SolidBrush(item.Color), x, y + 4)
    End Sub

    'A collection of ColorListBoxItems
    Public Class ColorListBoxItemCollection
        Inherits System.Collections.ObjectModel.Collection(Of ColorListBoxItem)

        'Keep a reference to the ColorListBox so we can update its baseItems list
        Private _listBox As ColorListBox

        Public Sub New(ByVal listBox As ColorListBox)
            _listBox = listBox
        End Sub

        Public Overloads Function Add(ByVal text As String) As ColorListBoxItem
            Return Me.Add(text, Color.Black, Nothing)
        End Function

        Public Overloads Function Add(ByVal text As String, ByVal color As Color) As ColorListBoxItem
            Return Me.Add(text, color, Nothing)
        End Function

        Public Overloads Function Add(ByVal text As String, ByVal color As Color, ByVal img As Image) As ColorListBoxItem
            Dim item As New ColorListBoxItem(text, color, img)
            Me.InsertItem(Me.Items.Count, item)
            Return item
        End Function

        Protected Overrides Sub ClearItems()
            MyBase.ClearItems()
            _listBox.baseItems.Clear()
        End Sub

        Protected Overrides Sub InsertItem(ByVal index As Integer, ByVal item As ColorListBoxItem)
            MyBase.InsertItem(index, item)
            _listBox.baseItems.Insert(index, item)
        End Sub

        Protected Overrides Sub RemoveItem(ByVal index As Integer)
            MyBase.RemoveItem(index)
            _listBox.baseItems.RemoveAt(index)
        End Sub

        Protected Overrides Sub SetItem(ByVal index As Integer, ByVal item As ColorListBoxItem)
            MyBase.SetItem(index, item)
            _listBox.baseItems(index) = item
        End Sub

        Public Sub AddRange(ByVal items As IEnumerable(Of ColorListBoxItem))
            For Each item As ColorListBoxItem In items
                Me.InsertItem(Me.Items.Count, item)
            Next
        End Sub

    End Class

    'A collection containing the selected items
    Public Class ColorListBoxSelectedItemCollection
        Inherits System.Collections.ObjectModel.Collection(Of ColorListBoxItem)
    End Class

End Class

'An item that is added to the ColorListBox
Public Class ColorListBoxItem

    Public Sub New()
        Me.New("New item", Color.Black, Nothing)
    End Sub

    Public Sub New(ByVal text As String)
        Me.New(text, Color.Black, Nothing)
    End Sub

    Public Sub New(ByVal text As String, ByVal color As Color)
        Me.New(text, color, Nothing)
    End Sub

    Public Sub New(ByVal text As String, ByVal color As Color, ByVal img As Image)
        Me.Text = text
        Me.Color = color
        Me.Image = img
    End Sub

    Private _IconsForecolor As Color
    Public Property Icons_Forecolor() As Color
        Get
            Return _IconsForecolor
        End Get
        Set(ByVal value As Color)
            _IconsForecolor = value
        End Set
    End Property

    Private _IconsFont As Font
    Public Property Icons_Font() As Font
        Get
            Return _IconsFont
        End Get
        Set(ByVal value As Font)
            _IconsFont = value
        End Set
    End Property

    Private _Text As String
    Public Property Text() As String
        Get
            Return _Text
        End Get
        Set(ByVal value As String)
            _Text = value
        End Set
    End Property

    Private _Color As Color
    Public Property Color() As Color
        Get
            Return _Color
        End Get
        Set(ByVal value As Color)
            _Color = value
        End Set
    End Property

    Private _Image As Image
    Public Property Image() As Image
        Get
            Return _Image
        End Get
        Set(ByVal value As Image)
            _Image = value
        End Set
    End Property

    Private _Icon1 As Image
    Public Property Icon1() As Image
        Get
            Return _Icon1
        End Get
        Set(ByVal value As Image)
            _Icon1 = value
        End Set
    End Property

    Private _Icon2 As Image
    Public Property Icon2() As Image
        Get
            Return _Icon2
        End Get
        Set(ByVal value As Image)
            _Icon2 = value
        End Set
    End Property

    Private _Icon3 As Image
    Public Property Icon3() As Image
        Get
            Return _Icon3
        End Get
        Set(ByVal value As Image)
            _Icon3 = value
        End Set
    End Property

    Private _Label1 As String
    Public Property Icon1_Label() As String
        Get
            Return _Label1
        End Get
        Set(ByVal value As String)
            _Label1 = value
        End Set
    End Property

    Private _Label2 As String
    Public Property Icon2_Label() As String
        Get
            Return _Label2
        End Get
        Set(ByVal value As String)
            _Label2 = value
        End Set
    End Property

    Private _Label3 As String
    Public Property Icon3_Label() As String
        Get
            Return _Label3
        End Get
        Set(ByVal value As String)
            _Label3 = value
        End Set
    End Property

    Private _tag As String
    Public Property Tag() As String
        Get
            Return _tag
        End Get
        Set(ByVal value As String)
            _tag = value
        End Set
    End Property
End Class
