Imports System
Imports System.Drawing
Imports System.ComponentModel
Imports System.Runtime.InteropServices
Imports System.Windows.Forms
Imports Mercury.Mobile.Forms.Primitives

Public Delegate Sub ComboDroppedDownEventHandler(ByVal sender As Object, ByVal e As System.EventArgs)

''' <summary>
''' Represents a combobox whose list elements can be highlighted based on the value of a property.
''' </summary>
''' <remarks></remarks>
Public Class MarkableComboBox
    Inherits Control

    ' Boolean states
    Private isPressed As Boolean = False
    Private isResize As Boolean = False
    Private _isListDroppedDown As Boolean = False
    Private isUserSpecifiedDropHeight As Boolean = False
    Private isUserSpecifiedDropWidth As Boolean = False
    Private isSelectedItemState As Boolean = False

    ' Main
    Private _selectedIndex As Integer = -1
    Friend WithEvents _listBox As MarkableListBox
    Friend WithEvents _textBox As TextBox
    Private _nativeDrawing As Win32.NativeDrawing = New Win32.NativeDrawing

    ' Attributes
    Private _controlText As String = String.Empty
    Private _dropDownHeight As Integer = 0
    Private _dropDownWidth As Integer = 0
    Private _maxViewableItems As Integer = 7

    ' Draw
    Private buttonWidth As Integer = 18
    Private glyphHeight As Integer = 5
    Private glyphWidth As Integer = 9

    Private rectContainer As Rectangle = New Rectangle(0, 0, 1, 1)
    Private rectButton As Rectangle = New Rectangle(0, 0, 1, 1)
    Private rectContent As Rectangle = New Rectangle(0, 0, 1, 1)
    Private rectGlyph As Rectangle = New Rectangle(0, 0, 1, 1)
    Private rectDropDown As Rectangle = New Rectangle(0, 0, 1, 1)

    Protected backBuffer As System.Drawing.Bitmap

    ' Color and Styles
    Private _dropDownStyle As ComboBoxStyle = ComboBoxStyle.DropDownList
    Private _font As Font = New Font("Tahoma", 10, FontStyle.Regular)
    Private _borderColor As Color = Color.Black

    ' Event delegates
    Public Event DroppedDown As ComboDroppedDownEventHandler
    Public Event SelectedIndexChanged As EventHandler

    ' Constraints
    Private parentHeightConstraint As Integer = 23

    ''' <summary>
    ''' Default constructor.
    ''' </summary>
    ''' <remarks></remarks>
    Public Sub New()
        ' Set default colors
        MyBase.BackColor = Color.FromArgb(255, 255, 255)
        MyBase.ForeColor = System.Drawing.SystemColors.WindowText

        ' Set font
        MyBase.Font = Me._font

        ' Set main control location
        Me.Location = New Point(0, 0)
        Me.Size = New Size(100, 23)

        ' Setup textbox
        Me.SuspendLayout()
        Me._textBox = New TextBox
        Me._textBox.BorderStyle = BorderStyle.None
        Me._textBox.TabIndex = 0
        Me._textBox.WordWrap = False
        Me._textBox.TextAlign = HorizontalAlignment.Left
        Me._textBox.Multiline = False
        Me._textBox.Font = Me._font
        Me._textBox.Text = String.Empty
        Me._textBox.Visible = False
        Me._textBox.Text = String.Empty
        Me.Controls.Add(Me._textBox)
        Me.ResumeLayout(False)

        ' Setup listbox
        Me.SuspendLayout()
        Me._listBox = New MarkableListBox
        Me._listBox.BindingContext = New BindingContext()
        Me._listBox.Location = New Point(0, Me.parentHeightConstraint - 1)
        Me._listBox.Size = New Size(100, 50)
        Me.Controls.Add(Me._listBox)
        Me.ResumeLayout(False)

        ' Init
        ControlInit()
    End Sub

#Region "Properties"

    ''' <summary>
    ''' Gets/sets the drop-down style for the ComboBox.
    ''' </summary>
    ''' <value>ComboBoxStyle.</value>
    ''' <returns>ComboBoxStyle.</returns>
    ''' <remarks></remarks>
    Public Property DropDownStyle() As ComboBoxStyle
        Get
            Return Me._dropDownStyle
        End Get
        Set(ByVal value As ComboBoxStyle)
            Me._dropDownStyle = value
            If Me._dropDownStyle = ComboBoxStyle.DropDownList Then
                'Me._textBox.Visible = False
            Else
                'Me._textBox.Visible = True
            End If
            Me.Invalidate()
        End Set
    End Property

    ''' <summary>
    ''' Gets/sets the font for the control.
    ''' </summary>
    ''' <value>System.Drawing.Font.</value>
    ''' <returns>System.Drawing.Font.</returns>
    ''' <remarks></remarks>
    Public Overrides Property Font() As System.Drawing.Font
        Get
            Return Me._font
        End Get
        Set(ByVal value As System.Drawing.Font)
            Me._font = value
            'Me._textBox.Font = value
            Me._listBox.Font = value
            Me.Invalidate()
        End Set
    End Property

    ''' <summary>
    ''' Gets/sets the drop-down list height.
    ''' </summary>
    ''' <value>Integer height.</value>
    ''' <returns>Integer height.</returns>
    ''' <remarks></remarks>
    Public Property DropDownHeight() As Integer
        Get
            Return Me._dropDownHeight
        End Get
        Set(ByVal value As Integer)
            Me._dropDownHeight = value
        End Set
    End Property

    ''' <summary>
    ''' Gets/sets the drop-down list width.
    ''' </summary>
    ''' <value>Integer width.</value>
    ''' <returns>Integer width.</returns>
    ''' <remarks></remarks>
    Public Property DropDownWidth() As Integer
        Get
            Return Me._dropDownWidth
        End Get
        Set(ByVal value As Integer)
            Me._dropDownWidth = value
        End Set
    End Property

    ''' <summary>
    ''' Gets/sets a value that determines if the
    ''' user specified value will be used to set
    ''' the drop-down height. 
    ''' </summary>
    ''' <value>Boolean.</value>
    ''' <returns>Boolean value.</returns>
    ''' <remarks></remarks>
    Public Property UserSpecifiedDropHeight() As Boolean
        Get
            Return Me.isUserSpecifiedDropHeight
        End Get
        Set(ByVal value As Boolean)
            Me.isUserSpecifiedDropHeight = value
        End Set
    End Property

    ''' <summary>
    ''' Gets/sets a value that determines if the
    ''' user specified value will be used to set
    ''' the drop-down width. 
    ''' </summary>
    ''' <value>Boolean.</value>
    ''' <returns>Boolean value.</returns>
    ''' <remarks></remarks>
    Public Property UserSpecifiedDropWidth() As Boolean
        Get
            Return Me.isUserSpecifiedDropWidth
        End Get
        Set(ByVal value As Boolean)
            Me.isUserSpecifiedDropWidth = value
        End Set
    End Property

    ' Behavior

    ''' <summary>
    ''' Gets/sets the state of the drop-down list.
    ''' </summary>
    ''' <value>Boolean drop-down state.</value>
    ''' <returns>Boolean drop-down state.</returns>
    ''' <remarks></remarks>
    Public Property IsDroppedDown() As Boolean
        Get
            Return Me._isListDroppedDown
        End Get
        Set(ByVal value As Boolean)
            Me._isListDroppedDown = value
            ' If the list is dropped down
            If Me._isListDroppedDown Then
                Me.Height = Me.parentHeightConstraint + (Me._dropDownHeight - 1)
            Else
                Me.Height = Me.parentHeightConstraint
            End If


            ' Invalidate
            Me.Invalidate()

            If Me._isListDroppedDown Then
                OnDroppedDown(EventArgs.Empty)
            End If
        End Set
    End Property

    ''' <summary>
    ''' Gets a value indicating whether the control, or one of 
    ''' its client controls, currently has the input focus.
    ''' </summary>
    ''' <value>Boolean focused.</value>
    ''' <returns>Boolean focused.</returns>
    ''' <remarks></remarks>
    Public ReadOnly Property ContainsFocus() As Boolean
        Get
            If Me.Focused Then
                Return True
            Else
                Return IsFocusedControl(Me)
            End If
        End Get
    End Property

    ''' <summary>
    ''' Gets/sets the datasource for the control.
    ''' </summary>
    ''' <value>System.Object datasource.</value>
    ''' <returns>System.Object datasource.</returns>
    ''' <remarks></remarks>
    Public Property DataSource() As Object
        Get
            Return Me._listBox.DataSource
        End Get
        Set(ByVal value As Object)
            Me._listBox.DataSource = value
        End Set
    End Property

    ''' <summary>
    ''' Gets/sets the display member for the control.
    ''' </summary>
    ''' <value>DisplayMember string.</value>
    ''' <returns>DisplayMember string.</returns>
    ''' <remarks></remarks>
    Public Property DisplayMember() As String
        Get
            Return Me._listBox.DisplayMember
        End Get
        Set(ByVal value As String)
            Me._listBox.DisplayMember = value
        End Set
    End Property

    ''' <summary>
    ''' Read-only property gets the items in the list control.
    ''' </summary>
    ''' <value>Asurion.Mobile.Presentation.DataSliceCollection items.</value>
    ''' <returns>Asurion.Mobile.Presentation.DataSliceCollection items.</returns>
    ''' <remarks></remarks>
    Public ReadOnly Property Items() As RowCollection
        Get
            Return Me._listBox.Items
        End Get
    End Property

    ''' <summary>
    ''' Gets/sets the zero-based index of the currently selected 
    ''' item in an Asurion.Mobile.Presentation.MobileComboBox.
    ''' </summary>
    ''' <value>Selected index integer.</value>
    ''' <returns>Selected index integer.</returns>
    ''' <remarks></remarks>
    Public Property SelectedIndex() As Integer
        Get
            Return Me._selectedIndex
        End Get
        Set(ByVal value As Integer)
            ' If listbox is not nothing
            If Me._listBox IsNot Nothing Then
                ' If no items in listbox
                If Me._listBox.Items.Count = 0 Then
                    Return
                End If
                ' If datasource exists and value is -1
                If Me._listBox.DataSource IsNot Nothing AndAlso value = -1 Then
                    Return
                End If

                ' If value within bounds
                If value <= (Me._listBox.Items.Count - 1) AndAlso value >= -1 Then
                    Me._selectedIndex = value
                    If Me._selectedIndex <> Me._listBox.SelectedIndex Then
                        Me._listBox.SelectedIndex = value
                    End If
                    Me._textBox.Text = Me._listBox.GetItemText(Me._listBox.SelectedItem)
                    ' Raise event
                    OnSelectedIndexChanged(EventArgs.Empty)
                End If
            End If
        End Set
    End Property

    ''' <summary>
    ''' Gets/sets the text associated with this control.
    ''' </summary>
    ''' <value>String text.</value>
    ''' <returns>String text.</returns>
    ''' <remarks></remarks>
    Public Overrides Property Text() As String
        Get
            Return Me._textBox.Text
        End Get
        Set(ByVal value As String)
            MyBase.Text = String.Empty
        End Set
    End Property

    ''' <summary>
    ''' Gets/sets the value determining whether the MobileComboBox
    ''' will highlight a drop-down row with a True value on the 
    ''' specified flag column.
    ''' </summary>
    ''' <value>Boolean.</value>
    ''' <returns>True if set to highlight flagged rows; otherwise, false.</returns>
    ''' <remarks></remarks>
    Public Property CanHighlightFlagRows() As Boolean
        Get
            Return Me._listBox.CanHighlightFlagRows
        End Get
        Set(ByVal value As Boolean)
            Me._listBox.CanHighlightFlagRows = value
        End Set
    End Property

    ''' <summary>
    ''' Gets/sets the name of the row flag column.
    ''' </summary>
    ''' <value>String.</value>
    ''' <returns>Flag column name.</returns>
    ''' <remarks></remarks>
    Public Property FlagColumnName() As String
        Get
            Return Me._listBox.FlagColumnName
        End Get
        Set(ByVal value As String)
            Me._listBox.FlagColumnName = value
        End Set
    End Property

    ''' <summary>
    ''' Gets/sets the color used for highlighting flagged rows.
    ''' </summary>
    ''' <value>System.Drawing.Color.</value>
    ''' <returns>System.Drawing.Color for highlighting flagged rows.</returns>
    ''' <remarks></remarks>
    Public Property FlagRowHighlightColor() As Color
        Get
            Return Me._listBox.FlagRowHighlightColor
        End Get
        Set(ByVal value As Color)
            Me._listBox.FlagRowHighlightColor = value
        End Set
    End Property

    ''' <summary>
    ''' Gets/sets the background color for the control.
    ''' </summary>
    ''' <value>System.Drawing.Color.</value>
    ''' <returns>System.Drawing.Color.</returns>
    ''' <remarks></remarks>
    Public Overrides Property BackColor() As Color
        Get
            Return MyBase.BackColor
        End Get
        Set(ByVal value As Color)
            MyBase.BackColor = value
            ' Reset brush
            Me.cacheBackColorBrush = New SolidBrush(Me.BackColor)
        End Set
    End Property

    ''' <summary>
    ''' Gets/sets the foreground color for the control.
    ''' </summary>
    ''' <value>System.Drawing.Color.</value>
    ''' <returns>System.Drawing.Color.</returns>
    ''' <remarks></remarks>
    Public Overrides Property ForeColor() As System.Drawing.Color
        Get
            Return MyBase.ForeColor
        End Get
        Set(ByVal value As System.Drawing.Color)
            MyBase.ForeColor = value
            ' Reset brush
            Me.cacheForeColorBrush = New SolidBrush(Me.ForeColor)
        End Set
    End Property

#End Region

#Region "Events"

    ' Overridable

    Protected Overridable Sub OnSelectedIndexChanged(ByVal e As System.EventArgs)
        ' If selected index within bounds
        If Me.SelectedIndex > -1 Then
            Me.IsDroppedDown = False
            Me.isSelectedItemState = True
            Me.Focus()
            RaiseEvent SelectedIndexChanged(Me, e)
        End If

        ' Invalidate
        Me.Invalidate()
    End Sub

    Protected Overridable Sub OnDroppedDown(ByVal e As System.EventArgs)
        Me.Focus()
        RaiseEvent DroppedDown(Me, e)
    End Sub

    ' Overrides

    Protected Overrides Sub OnGotFocus(ByVal e As System.EventArgs)
        Me.Invalidate()
        MyBase.OnGotFocus(e)
    End Sub

    Protected Overrides Sub OnLostFocus(ByVal e As System.EventArgs)
        ' If not focused
        If Not (Me.ContainsFocus) Then
            Me.isSelectedItemState = False
            Me.IsDroppedDown = False
            Me.Invalidate()
        End If
        MyBase.OnLostFocus(e)
    End Sub

    Protected Overrides Sub OnMouseDown(ByVal e As System.Windows.Forms.MouseEventArgs)
        ' If mouse-down within button client area
        If Me.RectangleToScreen(Me.rectButton).Contains(MousePosition) OrElse _
            Me.RectangleToScreen(Me.rectContent).Contains(MousePosition) Then
            ' Set is pressed
            Me.isPressed = True
            ' Set drop-down status
            If Me._isListDroppedDown Then
                Me.IsDroppedDown = False
            Else
                Me.IsDroppedDown = True
            End If
            ' Invalidate
            Me.Invalidate()
        End If
    End Sub

    Protected Overrides Sub OnMouseUp(ByVal e As System.Windows.Forms.MouseEventArgs)
        ' Set pressed to false
        Me.isPressed = False
        Me.Invalidate()
    End Sub

    Protected Overrides Sub OnClick(ByVal e As System.EventArgs)
        MyBase.OnClick(e)
    End Sub

    Protected Overrides Sub OnResize(ByVal e As System.EventArgs)
        ' If dropped down
        If Me._isListDroppedDown Then
            MyBase.Height = Me.Height
        Else
            ' Set parent height constraint
            MyBase.Height = Me.parentHeightConstraint
        End If

        If Me._textBox IsNot Nothing AndAlso Me._listBox IsNot Nothing Then
            CalculateClientArea()
        End If
    End Sub

    Protected Overrides Sub OnPaint(ByVal e As System.Windows.Forms.PaintEventArgs)
        ' Is buffer empty
        If Me.backBuffer Is Nothing Then
            Me.backBuffer = New Bitmap(Me.ClientSize.Width, Me.ClientSize.Height)
        End If

        ' Is buffer filled
        If Me.backBuffer IsNot Nothing Then
            ' Draw on buffer
            Using backBufferGraphicsContext As Graphics = Graphics.FromImage(Me.backBuffer)
                ' Clear
                backBufferGraphicsContext.Clear(Me.BackColor)
                ' Draw the control
                DrawControl(backBufferGraphicsContext)

                ' If selection state
                If Me.isSelectedItemState AndAlso Not (Me.IsDroppedDown) Then
                    Dim rectBounds As Rectangle = Me._textBox.Bounds
                    rectBounds.Offset(2, 1)
                    rectBounds.Height += 1
                    rectBounds.Width -= 4
                    Me._nativeDrawing.DrawFocusRectangle(backBufferGraphicsContext, rectBounds)
                End If
            End Using
            ' Draw in-memory image to screen
            e.Graphics.DrawImage(Me.backBuffer, 0, 0)
        End If
    End Sub

    Protected Overrides Sub OnPaintBackground(ByVal e As System.Windows.Forms.PaintEventArgs)
        ' Double buffering - do nothing
    End Sub

#End Region

#Region "Private Drawing Methods"

    ' Brush and pen cache
    Private cacheDropShadowPen As Pen
    Private cacheDarkTransitionPen As Pen
    Private cacheLightTransitionPen As Pen
    Private cacheHighlightPen As Pen
    Private cacheBackColorBrush As Brush
    Private cacheForeColorBrush As Brush
    Private cacheButtonBrush As Brush
    Private cacheGlyphBrush As Brush
    Private cacheHighlightSelectedBrush As Brush
    Private cacheHighlightSelectedTextBrush As Brush

    Private Sub ControlInit()
        ' Suspend layout
        Me.SuspendLayout()

        ' Resize custom control
        ' to the textbox
        Me.isResize = True
        CalculateClientArea()

        '' Resume layout
        Me.ResumeLayout(False)
        ' Inavlidate
        Me.Invalidate()
    End Sub

    Private Sub CalculateClientArea()
        ' Client padding
        Dim clientPaddingX As Integer = 2
        Dim clientPaddingY As Integer = 2

        ' Calculate container element
        Me.rectContainer = New Rectangle(Me.ClientRectangle.Left, Me.ClientRectangle.Top, _
                                         Me.ClientRectangle.Width, Me.parentHeightConstraint)

        ' Calculate drop-down rectangle
        CalculateDropArea()
        Me.rectDropDown = New Rectangle(Me.rectContainer.X, _
                                        (Me.rectContainer.Y + Me.rectContainer.Height) - 1, _
                                        Me._dropDownWidth, Me._dropDownHeight)
        Me._listBox.Location = New Point(Me.rectContainer.Left, (Me.rectContainer.Top + Me.rectContainer.Height) - 1)
        Me._listBox.Size = New Size(Me._dropDownWidth, Me._dropDownHeight)

        ' Calculate new content rectangle
        Me.rectContent = New Rectangle(clientPaddingX, clientPaddingY, _
                                       (Me.rectContainer.Width - ((clientPaddingX * 2) + 1)) _
                                            - Me.buttonWidth, _
                                       Me.rectContainer.Height - ((clientPaddingY * 2)))
        ' Calculate new button rectangle
        Me.rectButton = New Rectangle(Me.rectContent.Left + Me.rectContent.Width, _
                                      Me.rectContent.Top, Me.buttonWidth, Me.rectContent.Height - 1)
        ' Calculate arrow glyph rectangle
        Me.rectGlyph = New Rectangle((Me.rectButton.Left + CInt(Decimal.Truncate(Me.rectButton.Width / 2))) _
                                        - CInt(Decimal.Truncate(Me.glyphWidth / 2)), _
                                     Me.rectButton.Top + 7, Me.glyphWidth, Me.glyphHeight)

        ' Calculate textbox size and location
        Me._textBox.Location = New Point(Me.rectContent.X, Me.rectContent.Y)
        Me._textBox.Size = New Size(Me.rectContent.Width, Me.rectContent.Height)
    End Sub

    Private Sub CalculateDropArea()
        ' Get drop-down width
        If Not (Me.isUserSpecifiedDropWidth) Then
            Me._dropDownWidth = Me.Width
        End If
        ' Get drop-down height
        If Me._listBox.Items.Count > 1 Then
            If Not (Me.isUserSpecifiedDropHeight) Then
                ' Measurements
                Dim maxContainerHeight As Integer = Fix(Me._maxViewableItems * Me._listBox.GetItemHeight)
                Dim toScreenListControlTop As Integer = Me.RectangleToScreen(Me.ClientRectangle).Top + _
                                                        (Me.parentHeightConstraint - 1)
                ' If item count is less than max viewable items
                If Me._listBox.Items.Count <= Me._maxViewableItems Then
                    Me._dropDownHeight = Fix(Me._listBox.Items.Count * Me._listBox.GetItemHeight) + 2
                ElseIf (toScreenListControlTop + maxContainerHeight) <= Screen.PrimaryScreen.Bounds.Height Then
                    Me._dropDownHeight = maxContainerHeight
                Else    ' Attempt to fill available screen area
                    Dim screenHeight As Integer = Screen.PrimaryScreen.Bounds.Height
                    Dim tempHeight As Integer = _
                                        Fix((screenHeight - toScreenListControlTop) / Me._listBox.GetItemHeight)
                    If tempHeight >= Me._listBox.GetItemHeight Then
                        Me._dropDownHeight = tempHeight
                    Else
                        Me._dropDownHeight = Me._listBox.GetItemHeight
                    End If
                End If
            End If
        Else
            Me._dropDownHeight = Me.parentHeightConstraint
        End If
    End Sub

    Private Sub DrawControl(ByVal graphicsContext As Graphics)
        ' Set textbox visibility
        Me._textBox.Visible = False
        ' If is dropped down
        If Me._isListDroppedDown Then
            Me._listBox.Visible = True
        Else
            Me._listBox.Visible = False
        End If

        ' Check cache for pens and brushes
        If Me.cacheDropShadowPen Is Nothing Then
            Me.cacheDropShadowPen = New Pen(Color.FromArgb(0, 0, 0))
        End If
        If Me.cacheDarkTransitionPen Is Nothing Then
            Me.cacheDarkTransitionPen = New Pen(Color.FromArgb(120, 120, 120))
        End If
        If Me.cacheLightTransitionPen Is Nothing Then
            Me.cacheLightTransitionPen = New Pen(Color.FromArgb(220, 220, 220))
        End If
        If Me.cacheHighlightPen Is Nothing Then
            Me.cacheHighlightPen = New Pen(Color.FromArgb(255, 255, 255))
        End If
        If Me.cacheBackColorBrush Is Nothing Then
            Me.cacheBackColorBrush = New System.Drawing.SolidBrush(MyBase.BackColor)
        End If
        If Me.cacheForeColorBrush Is Nothing Then
            Me.cacheForeColorBrush = New System.Drawing.SolidBrush(MyBase.ForeColor)
        End If
        If Me.cacheButtonBrush Is Nothing Then
            Me.cacheButtonBrush = New System.Drawing.SolidBrush(Color.Silver)
        End If
        If Me.cacheGlyphBrush Is Nothing Then
            Me.cacheGlyphBrush = New SolidBrush(Color.FromArgb(0, 0, 0))
        End If
        If Me.cacheHighlightSelectedBrush Is Nothing Then
            Me.cacheHighlightSelectedBrush = New SolidBrush(System.Drawing.SystemColors.Highlight)
        End If
        If Me.cacheHighlightSelectedTextBrush Is Nothing Then
            Me.cacheHighlightSelectedTextBrush = New SolidBrush(System.Drawing.SystemColors.HighlightText)
        End If

        ' Draw control shell
        DrawControlShell(graphicsContext)

        ' Draw content area
        DrawContentArea(graphicsContext)

        ' Draw button area
        DrawButtonArea(graphicsContext)
    End Sub

    Private Sub DrawControlShell(ByVal context As Graphics)
        ' Draw inner shadow
        context.DrawLine(Me.cacheDropShadowPen, Me.rectContainer.X + 1, Me.rectContainer.Y + 1, _
                            Me.rectContainer.X + 1, (Me.rectContainer.Y + Me.rectContainer.Height) - 1)
        context.DrawLine(Me.cacheDropShadowPen, Me.rectContainer.X + 1, Me.rectContainer.Y + 1, _
                            (Me.rectContainer.X + Me.rectContainer.Width) - 1, Me.rectContainer.Y + 1)

        ' Draw inner (light) shading
        context.DrawLine(Me.cacheLightTransitionPen, Me.rectContainer.X + 1, (Me.rectContainer.Y + _
                            Me.rectContainer.Height) - 2, (Me.rectContainer.X + _
                            Me.rectContainer.Width) - 2, (Me.rectContainer.Y + _
                            Me.rectContainer.Height) - 2)
        context.DrawLine(Me.cacheLightTransitionPen, (Me.rectContainer.X + Me.rectContainer.Width) - 2, _
                            Me.rectContainer.Y, (Me.rectContainer.X + Me.rectContainer.Width) - 2, _
                            (Me.rectContainer.Y + Me.rectContainer.Height) - 2)

        ' Draw outer (dark) shading
        context.DrawLine(Me.cacheDarkTransitionPen, Me.rectContainer.X, Me.rectContainer.Y, _
                            Me.rectContainer.X + Me.rectContainer.Width, Me.rectContainer.Y)
        context.DrawLine(Me.cacheDarkTransitionPen, Me.rectContainer.X, Me.rectContainer.Y, _
                            Me.rectContainer.X, Me.rectContainer.Y + Me.rectContainer.Height)

        ' Draw outer highlight
        context.DrawLine(Me.cacheHighlightPen, Me.rectContainer.X, (Me.rectContainer.Y + _
                            Me.rectContainer.Height) - 1, (Me.rectContainer.X + Me.rectContainer.Width) - 1, _
                            (Me.rectContainer.Y + Me.rectContainer.Height) - 1)
        context.DrawLine(Me.cacheHighlightPen, (Me.rectContainer.X + Me.rectContainer.Width) - 1, _
                            Me.rectContainer.Y, (Me.rectContainer.X + Me.rectContainer.Width) - 1, _
                            (Me.rectContainer.Y + Me.rectContainer.Height) - 1)
    End Sub

    Private Sub DrawContentArea(ByVal context As Graphics)
        ' Draw content fill
        context.FillRectangle(Me.cacheBackColorBrush, rectContent.X, rectContent.Y, _
                              rectContent.Width, rectContent.Height)

        ' Draw text
        If Me._dropDownStyle = ComboBoxStyle.DropDownList Then
            ' Set disable format
            Dim sFormat As StringFormat = New StringFormat(StringFormatFlags.NoWrap)
            sFormat.Alignment = StringAlignment.Near

            ' Get bounds
            Dim rectTextBounds As Rectangle = Me._textBox.Bounds
            rectTextBounds.Offset(2, 2)
            Dim rectSelectionBounds As Rectangle = Me._textBox.Bounds
            rectSelectionBounds.Offset(2, 1)

            ' Set color
            Dim textBrush As Brush
            If Me.isSelectedItemState AndAlso Not (Me.IsDroppedDown) Then
                textBrush = Me.cacheHighlightSelectedTextBrush
                context.FillRectangle(Me.cacheHighlightSelectedBrush, rectSelectionBounds.X, _
                                      rectSelectionBounds.Y, rectSelectionBounds.Width - 4, _
                                      rectSelectionBounds.Height + 1)
            Else
                textBrush = Me.cacheForeColorBrush
            End If

            ' If control is enabled
            If Me.Enabled Then
                context.DrawString(Me._textBox.Text, Me.Font, textBrush, rectTextBounds, sFormat)
            Else
                Dim disabledForeBrush As Brush = New SolidBrush(Color.FromArgb(240, 240, 240))
                context.DrawString(Me._textBox.Text, Me.Font, disabledForeBrush, rectTextBounds, sFormat)
            End If
        End If
    End Sub

    Private Sub DrawButtonArea(ByVal context As Graphics)
        ' Background fill
        context.FillRectangle(Me.cacheButtonBrush, Me.rectButton.X, Me.rectButton.Y, _
                                 Me.rectButton.Width, Me.rectButton.Height)

        ' Draw inner highlight
        Dim highlightPen As Pen
        If Me.isPressed Then
            highlightPen = Me.cacheDropShadowPen
        Else
            highlightPen = Me.cacheLightTransitionPen
        End If

        context.DrawLine(highlightPen, Me.rectButton.X + 1, Me.rectButton.Y + 1, _
                            Me.rectButton.X + 1, (Me.rectButton.Y + Me.rectButton.Height) - 2)
        context.DrawLine(highlightPen, Me.rectButton.X + 1, Me.rectButton.Y + 1, _
                            (Me.rectButton.X + Me.rectButton.Width) - 1, Me.rectButton.Y + 1)

        ' Draw inner (dark) shading
        Dim dkShadingPen As Pen
        If Me.isPressed Then
            dkShadingPen = Me.cacheDropShadowPen
        Else
            dkShadingPen = Me.cacheDarkTransitionPen
        End If

        context.DrawLine(dkShadingPen, Me.rectButton.X, Me.rectButton.Y, _
                            Me.rectButton.X, (Me.rectButton.Y + Me.rectButton.Height) - 1)
        context.DrawLine(dkShadingPen, Me.rectButton.X, Me.rectButton.Y, _
                            (Me.rectButton.X + Me.rectButton.Width) - 1, Me.rectButton.Y)

        If Me.isPressed Then
            context.DrawLine(Me.cacheLightTransitionPen, (Me.rectButton.X + Me.rectButton.Width) - 1, Me.rectButton.Y, _
                                (Me.rectButton.X + Me.rectButton.Width) - 1, _
                                (Me.rectButton.Y + Me.rectButton.Height) - 1)
            context.DrawLine(Me.cacheLightTransitionPen, Me.rectButton.X, _
                                (Me.rectButton.Y + Me.rectButton.Height) - 1, _
                                (Me.rectButton.X + Me.rectButton.Width) - 1, _
                                (Me.rectButton.Y + Me.rectButton.Height) - 1)
        Else
            context.DrawLine(dkShadingPen, (Me.rectButton.X + Me.rectButton.Width) - 1, Me.rectButton.Y, _
                                (Me.rectButton.X + Me.rectButton.Width) - 1, _
                                (Me.rectButton.Y + Me.rectButton.Height) - 1)
            context.DrawLine(dkShadingPen, Me.rectButton.X, _
                                (Me.rectButton.Y + Me.rectButton.Height) - 1, _
                                (Me.rectButton.X + Me.rectButton.Width) - 1, _
                                (Me.rectButton.Y + Me.rectButton.Height) - 1)
        End If

        ' Draw shadow
        Dim shadowPen As Pen
        If Me.isPressed Then
            shadowPen = Me.cacheHighlightPen
        Else
            shadowPen = Me.cacheDropShadowPen
        End If

        context.DrawLine(shadowPen, Me.rectButton.X, Me.rectButton.Y + Me.rectButton.Height, _
                            Me.rectButton.X + Me.rectButton.Width, _
                            Me.rectButton.Y + Me.rectButton.Height)
        context.DrawLine(shadowPen, Me.rectButton.X + Me.rectButton.Width, Me.rectButton.Y, _
                            Me.rectButton.X + Me.rectButton.Width, _
                            Me.rectButton.Y + Me.rectButton.Height)

        ' Draw arrow glyph
        DrawArrowGlyph(context)
    End Sub

    Private Sub DrawArrowGlyph(ByVal context As Graphics)
        ' Get arrow points
        Dim glyphPoints(2) As Point
        glyphPoints(0) = New Point(Me.rectGlyph.Left, Me.rectGlyph.Top)
        glyphPoints(1) = New Point(Me.rectGlyph.Left + Me.rectGlyph.Width, Me.rectGlyph.Top)
        glyphPoints(2) = New Point(CInt(Decimal.Truncate(Me.rectGlyph.Left + (Me.rectGlyph.Width / 2))), Me.rectGlyph.Bottom)

        ' Draw glyph
        context.FillPolygon(Me.cacheGlyphBrush, glyphPoints)
    End Sub

#End Region

#Region "Child Control Event Handlers"

    Private Sub _textBox_OnGotFocus(ByVal sender As Object, ByVal e As System.EventArgs) Handles _textBox.GotFocus
        OnGotFocus(e)
    End Sub

    Private Sub _textBox_KeyPress(ByVal sender As Object, ByVal e As KeyPressEventArgs) Handles _textBox.KeyPress
        OnKeyPress(e)
    End Sub

    Private Sub _textBox_OnKeyDown(ByVal sender As Object, ByVal e As KeyEventArgs) Handles _textBox.KeyDown
        e.Handled = True
    End Sub

    Private Sub _textBox_OnTextChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles _textBox.TextChanged
        OnTextChanged(e)
    End Sub

    Private Sub _listBox_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles _listBox.SelectedIndexChanged
        Me.SelectedIndex = Me._listBox.SelectedIndex
    End Sub

#End Region

    Private Function IsFocusedControl(ByVal container As Control) As Boolean
        For Each childControl As Control In container.Controls
            If childControl.Focused Then
                Return True
            End If
        Next

        For Each childControl As Control In container.Controls
            Dim possibleFocus As Boolean = IsFocusedControl(childControl)
            If possibleFocus = True Then
                Return possibleFocus
            End If
        Next

        ' Default false
        Return False
    End Function

End Class
