Click here to Skip to main content
15,896,606 members
Articles / Programming Languages / Visual Basic

Flatten that Combobox!

Rate me:
Please Sign up or sign in to vote.
4.24/5 (19 votes)
2 Feb 20035 min read 276.8K   3.5K   25  
A flat style combobox in VB.NET
Imports System.Security
Imports System.Runtime.InteropServices

''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Author: Stacey Brown (ibanezowner@yahoo.com)
' Date: June, 2002
' Reason: Bored
' URL: staceybrown.hispeed.com (I gave up on this site back around 1998)
'
' Overview:
'   In .NET, Microsoft has given us many things to work 
' with. It's nice how they standardized properties like 
' Text and Name on most controls. owever, what's the story 
' with the Flat look?
'
'   Some controls have a flat look as a property, either 
' through the FlatStyle property or the Border property. 
' I love the flat look and have been using it for all my 
' controls, except one. The combobox. Do you know how funny 
' a data entry form looks when all its text boxes, checkboxes, 
' radio buttons, command buttons, etc are flat, but its 
' combo boxes are 3D? You probably do, and that's why
' you're looking here...
'
'   This project is nothing more than a simple combobox that 
' inherits from combobox. All events and properties of the 
' regular combo box are there. It even does databinding. The 
' great thing is that it's flat.
'
'   I didn't exactly write this code. I'm a VB developer mostly, 
' and at work we're almost 100% VB. I needed to find a combobox 
' control with a flat look written in VB. What did I find? I 
' found the coolest code on CodeProject.com in an article named, 
' "VSNetToolbar (flat toolbar with embedded combo boxes) written in C#" 
' by Carlos H. Perez. It's a incredibly awesome project to have. 
' However, it's not in VB. I ported his code, line for line, 
' coming up with my own VB equivalents for things like bit 
' shifting, etc. I converted all the API calls and structures 
' needed, also. This was a lot of fun to convert.
'
'   If you like the code, thank Carlos. If you like the fact 
' that it's in VB, thank me! Enjoy..
''''''''''''''''''''''''''''''''''''''''''''''''''''''''

Public Class FlatComboBase
    '''''''''''''''''''''''''''''''''''''
    ' This class serves as a base class
    ' handling most of the events from
    ' combobox. It can be further inherited
    ' and extended. In fact, in Carlos'
    ' C# project, he creates flat dropdowns
    ' with bitmaps in them!
    '''''''''''''''''''''''''''''''''''''
    Inherits System.Windows.Forms.ComboBox
    Private eraseDropDownHightLight As Boolean = False
    Private mouseHookHandle As IntPtr = IntPtr.Zero
    Private mouseProcHandle As GCHandle
    Public toolBarUse As Boolean = False
    Private hooked As Boolean = False
    Public Const ARROW_WIDTH As Integer = 13
    Private editHook As EditCtrlHook = Nothing
    Friend forceUpdate As Boolean = False
    Friend highlighted As Boolean = False

#Region " Public methods"
    Public Sub New(ByVal toolBarUse As Boolean)
        '// Flag to indicate that combo box will be used
        '// in a toolbar --which means we will use some window hooks
        '// to reset the focus of the combobox--
        Me.toolBarUse = toolBarUse
        DrawMode = DrawMode.OwnerDrawFixed
        SetStyle(ControlStyles.AllPaintingInWmPaint Or ControlStyles.UserPaint Or ControlStyles.Opaque, True)
        '// Use Menu font so that the combobox uses the same font as the toolbar buttons
        Font = SystemInformation.MenuFont
        '// When use in a toolbar we don't need tab stop
        TabStop = False
    End Sub

    Public Sub New()
        DrawMode = DrawMode.OwnerDrawFixed
        SetStyle(ControlStyles.AllPaintingInWmPaint Or ControlStyles.UserPaint Or ControlStyles.Opaque, True)
    End Sub

    Public Sub SetFontHeight(ByVal newHeight As Integer)
        FontHeight = newHeight
    End Sub
#End Region

#Region " Protected methods"
    Protected Overrides Sub OnHandleCreated(ByVal e As EventArgs)
        MyBase.OnHandleCreated(e)
        '// Hook the edit control
        If DropDownStyle = ComboBoxStyle.DropDown Then
            Dim hEditControl As IntPtr = Win32API.GetDlgItem(Handle, &H3E9)
            Debug.Assert(Not hEditControl.ToInt64 = IntPtr.Zero.ToInt64, "Fail to get ComboBox's Edit Control Handle...")
            editHook = New EditCtrlHook(Me)
            editHook.AssignHandle(hEditControl)
        End If
    End Sub

    Protected Overrides Sub OnDrawItem(ByVal e As DrawItemEventArgs)
        '// Draw bitmap strech to the size of the size of the combobox
        Dim g As Graphics = e.Graphics
        Dim bounds As Rectangle = e.Bounds
        Dim selected As Boolean = (e.State And DrawItemState.Selected) > 0
        Dim editSel As Boolean = (e.State & DrawItemState.ComboBoxEdit) > 0
        If e.Index <> -1 Then
            DrawComboBoxItem(g, bounds, e.Index, selected, editSel)
        End If
    End Sub

    Protected Overrides Sub OnPaint(ByVal pe As PaintEventArgs)
        '// This on paint is only going to happen for the combobox if 
        '// the combobox has been set the style to do all painting
        '// in the OnPaint event
        PaintComboBoxBackground(pe.Graphics, SystemColors.Window)
        MyBase.OnPaint(pe)
        If DropDownStyle = ComboBoxStyle.DropDown Then
            '// We will handle the painting from WM_PAINT
            Exit Sub
        End If

        If Not Enabled Then
            DrawDisableState()
            Exit Sub
        End If

        Dim rc As Rectangle = ClientRectangle
        If SelectedIndex = -1 Then
            If Items.Count > 0 Then
                '// Select first item as the current item
                SelectedIndex = 0
            End If
        End If

        DrawComboBoxItemEx(pe.Graphics, rc, SelectedIndex, False, True)
        If Not ContainsFocus Then
            DrawComboBoxBorder(pe.Graphics, SystemColors.Window)
            DrawComboBoxArrowNormal(pe.Graphics, True)
        Else
            DrawComboBoxBorder(pe.Graphics, SystemColors.Highlight)
        End If
    End Sub

    Protected Overrides Sub WndProc(ByRef m As Message)
        Dim doPainting As Boolean = False
        Select Case m.Msg
            Case Win32API.Msg.WM_PAINT
                doPainting = True
                Exit Select
            Case Else
                Exit Select
        End Select
        MyBase.WndProc(m)

        '// Now let's do our own painting
        '// we have to do it after the combox
        '// does its own painting so that we can 
        '// let the edit control in the combobox
        '// take care of the text
        If doPainting Then ForcePaint(m)
    End Sub

    Protected Overrides Sub OnMouseEnter(ByVal e As EventArgs)
        MyBase.OnMouseEnter(e)
        Dim g As Graphics = CreateGraphics()
        DrawComboBoxBorder(g, SystemColors.Highlight)
        DrawComboBoxArrowSelected(g, False)
        g.Dispose()
    End Sub

    Protected Overrides Sub OnMouseLeave(ByVal e As EventArgs)
        MyBase.OnMouseLeave(e)
        If Not ContainsFocus Then
            Dim g As Graphics = CreateGraphics()
            DrawComboBoxBorder(g, SystemColors.Window)
            DrawComboBoxArrowNormal(g, False)
            g.Dispose()
        End If
    End Sub

    Protected Overrides Sub OnLostFocus(ByVal e As EventArgs)
        MyBase.OnLostFocus(e)
        Dim g As Graphics = CreateGraphics()
        DrawComboBoxBorder(g, SystemColors.Window)
        DrawComboBoxArrowNormal(g, False)
        g.Dispose()
        If toolBarUse And hooked Then
            hooked = False
            EndHook()
        End If
    End Sub

    Protected Overrides Sub OnGotFocus(ByVal e As EventArgs)
        MyBase.OnGotFocus(e)
        Dim g As Graphics = CreateGraphics()
        DrawComboBoxBorder(g, SystemColors.Highlight)
        DrawComboBoxArrowSelected(g, False)
        g.Dispose()
        If toolBarUse And Not hooked Then
            hooked = True
            StartHook()
        End If
    End Sub

    Protected Overrides Sub OnDropDown(ByVal e As EventArgs)
        eraseDropDownHightLight = True
        MyBase.OnDropDown(e)
    End Sub

    Protected Overrides Sub OnSelectedIndexChanged(ByVal e As EventArgs)
        MyBase.OnSelectedIndexChanged(e)
        If eraseDropDownHightLight Then
            Dim g As Graphics = CreateGraphics()
            DrawComboBoxArrowSelected(g, False)
            eraseDropDownHightLight = False
            g.Dispose()
        End If
    End Sub

    Protected Overridable Sub DrawComboBoxItem(ByVal g As Graphics, ByVal bounds As Rectangle, ByVal Index As Integer, ByVal selected As Boolean, ByVal editSel As Boolean)
        '// Draw the the combo item
        g.FillRectangle(New SolidBrush(SystemColors.Window), bounds.Left, bounds.Top, bounds.Width, bounds.Height)

        If selected And Not editSel Then
            '// Draw highlight rectangle
            g.FillRectangle(New SolidBrush(SystemColors.Highlight), bounds.Left, bounds.Top, bounds.Width, bounds.Height)
        Else
            '// Erase highlight rectangle
            g.FillRectangle(New SolidBrush(SystemColors.Window), bounds.Left, bounds.Top, bounds.Width, bounds.Height)

            If editSel And ContainsFocus Then
                '// Draw higlighted arrow
                DrawComboBoxArrowSelected(g, False)
            End If
        End If
    End Sub

    Protected Overridable Sub DrawComboBoxItemEx(ByVal g As Graphics, ByVal bounds As Rectangle, ByVal Index As Integer, ByVal selected As Boolean, ByVal editSel As Boolean)
        '// This function is only called form the OnPaint handler and the Graphics object passed is the one
        '// for the combobox itself as opossed to the one for the edit control in the combobox
        '// doing this allows us to be able to avoid clipping problems with text strings

        '// Draw the the combo item
        bounds.Inflate(-3, -3)
        g.FillRectangle(New SolidBrush(SystemColors.Window), bounds.Left, bounds.Top, bounds.Width, bounds.Height)

        If selected And Not editSel Then
            '// Draw highlight rectangle
            g.FillRectangle(New SolidBrush(SystemColors.Highlight), bounds.Left, bounds.Top, bounds.Width, bounds.Height)
        Else
            '// Erase highlight rectangle
            g.FillRectangle(New SolidBrush(SystemColors.Window), bounds.Left, bounds.Top, bounds.Width, bounds.Height)
            If editSel And ContainsFocus Then
                '// Draw higlighted arrow
                DrawComboBoxArrowSelected(g, False)
            End If
        End If
    End Sub
#End Region

#Region " Friend methods"
    Friend Sub DrawComboBoxBorder(ByVal g As Graphics, ByVal color As Color)
        '// Keep track of what we painted last
        If color.Equals(SystemColors.Highlight) Then
            highlighted = True
        Else
            highlighted = False
        End If

        Dim pen As Pen = New Pen(New SolidBrush(color), 1)
        g.DrawRectangle(pen, ClientRectangle.Left, ClientRectangle.Top, ClientRectangle.Width - 1, ClientRectangle.Height - 1)

        '// We need to draw an extra "inner" border to erase the ugly 3D look of  the combobox
        g.DrawRectangle(Pens.White, ClientRectangle.Left + 1, _
                        ClientRectangle.Top + 1, _
                        ClientRectangle.Width - SystemInformation.VerticalScrollBarWidth - 1, _
                        ClientRectangle.Height - 3)
    End Sub

    Friend Sub DrawComboBoxArrowNormal(ByVal g As Graphics, ByVal disable As Boolean)
        Dim left As Integer
        Dim top As Integer
        Dim arrowWidth As Integer
        Dim height As Integer

        CalculateArrowBoxCoordinates(left, top, arrowWidth, height)

        '// We are not going to draw the arrow background using the total
        '// width of the arrow button in the combobox because it too wide
        '// and it does not look nice. However, we need to paint over the section
        '// that correspond to the "original" arrow button dimension to avoid
        '// clipping or painting problems
        Dim stripeColorBrush As Brush = New SolidBrush(ColorUtil.VSNetStripeColor)
        If Enabled Then
            Dim Width As Integer = SystemInformation.VerticalScrollBarWidth - ARROW_WIDTH
            g.FillRectangle(Brushes.White, _
                            New Rectangle(left - Width, _
                                          top, _
                                          SystemInformation.VerticalScrollBarWidth, _
                                          height))
        End If

        If Not disable Then
            '// Erase previous selected rectangle first
            DrawComboBoxArrowSelected(g, True)
            '// Now draw the unselected background
            g.FillRectangle(stripeColorBrush, left, top, arrowWidth, height)
        Else
            '// Now draw the unselected background
            g.FillRectangle(stripeColorBrush, left - 1, top - 1, arrowWidth + 2, height + 2)
        End If
        DrawArrowGlyph(g, disable)
    End Sub

    Friend Sub DrawComboBoxArrowSelected(ByVal g As Graphics, ByVal bErase As Boolean)
        Dim left As Integer
        Dim top As Integer
        Dim arrowWidth As Integer
        Dim height As Integer

        CalculateArrowBoxCoordinates(left, top, arrowWidth, height)

        '// We are not going to draw the arrow background using the total
        '// width of the arrow button in the combobox because it too wide
        '// and it does not look nice. However, we need to paint over the section
        '// that correspond to the "original" arrow button dimension to avoid
        '// clipping or painting problems
        If Enabled Then
            Dim width As Integer = SystemInformation.VerticalScrollBarWidth - ARROW_WIDTH
            g.FillRectangle(Brushes.White, _
                            New Rectangle(left - width, _
                                          top, _
                                          SystemInformation.VerticalScrollBarWidth, _
                                          height))
        End If

        If Not bErase Then
            If (DroppedDown) Then
                '// If showing the list portion of the combo box, draw the arrow portion background using
                '// the highlight color with some transparency
                '// Don't use the graphics object that we get passed because that is associated
                '// to the edit control of the combobox and we actually want to paint on the combobox area
                '// and not be clipped only to the edit control client area
                Dim cbg As Graphics = CreateGraphics()
                cbg.FillRectangle(New SolidBrush(ColorUtil.VSNetPressedColor), left - 1, top - 1, arrowWidth + 2, height + 2)
                cbg.DrawRectangle(New Pen(New SolidBrush(SystemColors.Highlight), 1), left - 1, top - 1, arrowWidth + 2, height + 3)
                cbg.Dispose()
                forceUpdate = True
            Else
                g.FillRectangle(New SolidBrush(ColorUtil.VSNetSelectionColor), left - 1, top - 1, arrowWidth + 2, height + 2)
                g.DrawRectangle(New Pen(New SolidBrush(SystemColors.Highlight), 1), left - 1, top - 2, arrowWidth + 2, height + 3)
            End If
        Else
            g.FillRectangle(Brushes.White, left - 1, top - 1, arrowWidth + 2, height + 2)
        End If
        DrawArrowGlyph(g, False)
    End Sub

    Friend Sub DrawDisableState()
        Dim g As Graphics = CreateGraphics()
        PaintComboBoxBackground(g, SystemColors.Window)
        DrawComboBoxBorder(g, SystemColors.ControlDark)
        DrawComboBoxArrowNormal(g, True)
        g.Dispose()
    End Sub

    Friend Sub ForceTheUpdate()
        Dim g As Graphics = CreateGraphics()

        If ContainsFocus Then DrawComboBoxArrowSelected(g, False)
    End Sub
#End Region

#Region " Private methods"
    Private Sub PaintComboBoxBackground(ByVal g As Graphics, ByVal backColor As Color)
        Dim rc As Rectangle = ClientRectangle
        rc.Inflate(-1, -1)
        g.FillRectangle(New SolidBrush(backColor), rc)
    End Sub

    Private Sub CalculateArrowBoxCoordinates(ByRef left As Integer, ByRef top As Integer, ByRef width As Integer, ByRef height As Integer)
        Dim rc As Rectangle = ClientRectangle
        width = ARROW_WIDTH
        left = rc.Right - width - 2
        top = rc.Top + 2
        height = rc.Height - 4
    End Sub

    Private Sub DrawArrowGlyph(ByVal g As Graphics, ByVal disable As Boolean)
        Dim left As Integer
        Dim top As Integer
        Dim arrowWidth As Integer
        Dim height As Integer

        CalculateArrowBoxCoordinates(left, top, arrowWidth, height)

        '// Draw arrow glyph
        Dim pts(2) As Point
        pts(0) = New Point(left + arrowWidth / 2 - 2, top + height / 2 - 1)
        pts(1) = New Point(left + arrowWidth / 2 + 3, top + height / 2 - 1)
        pts(2) = New Point(left + arrowWidth / 2, (top + height / 2 - 1) + 3)

        If disable Then
            g.FillPolygon(New SolidBrush(SystemColors.ControlDark), pts)
        Else
            g.FillPolygon(Brushes.Black, pts)
        End If
    End Sub

    Private Sub ForcePaint(ByRef m As Message)
        '// Similar code to the OnPaint handler
        Dim g As Graphics = Graphics.FromHwnd(Handle)
        If Not Enabled Then
            DrawDisableState()
            Exit Sub
        End If

        Dim rc As Rectangle = ClientRectangle
        If SelectedIndex = -1 Then
            If Items.Count > 0 Then
                SelectedIndex = 0
            End If

            DrawComboBoxItemEx(g, rc, SelectedIndex, False, True)

            If Not ContainsFocus Then
                DrawComboBoxBorder(g, SystemColors.Window)
                DrawComboBoxArrowNormal(g, False)
            End If
        Else
            DrawComboBoxBorder(g, SystemColors.Highlight)
        End If
    End Sub

    Private Sub StartHook()
        '// Mouse hook
        Dim mouseHookProc As Win32API.HookProc = New Win32API.HookProc(AddressOf MouseHook)
        mouseProcHandle = GCHandle.Alloc(mouseHookProc)
        mouseHookHandle = Win32API.SetWindowsHookEx(Win32API.WindowsHookCodes.WH_MOUSE, mouseHookProc, IntPtr.Zero, Win32API.GetCurrentThreadId())
        If mouseHookHandle.ToInt64 = IntPtr.Zero.ToInt64 Then
            Throw New SecurityException()
        End If
    End Sub

    Private Sub EndHook()
        '// Unhook		
        Win32API.UnhookWindowsHookEx(mouseHookHandle)
        mouseProcHandle.Free()
        mouseHookHandle = IntPtr.Zero
    End Sub

    Private Function MouseHook(ByVal code As Integer, ByVal wparam As IntPtr, ByVal lparam As IntPtr) As IntPtr
        Dim mh As Win32API.MOUSEHOOKSTRUCT = Marshal.PtrToStructure(lparam, GetType(Win32API.MOUSEHOOKSTRUCT))
        If mh.hwnd.ToInt64 <> Handle.ToInt64 And Not DroppedDown And (wparam.ToInt64 = Convert.ToInt64(Win32API.Msg.WM_LBUTTONDOWN) Or wparam.ToInt64 = Convert.ToInt64(Win32API.Msg.WM_RBUTTONDOWN)) Or wparam.ToInt64 = Convert.ToInt64(Win32API.Msg.WM_NCLBUTTONDOWN) Then
            '// Loose focus
            Win32API.SetFocus(IntPtr.Zero)
        ElseIf mh.hwnd.ToInt64 <> Handle.ToInt64 And Not DroppedDown And (wparam.ToInt64 = Convert.ToInt64(Win32API.Msg.WM_LBUTTONUP) Or wparam.ToInt64 = Convert.ToInt64(Win32API.Msg.WM_RBUTTONUP)) Or wparam.ToInt64 = Convert.ToInt64(Win32API.Msg.WM_NCLBUTTONUP) Then
            Win32API.SetFocus(IntPtr.Zero)
        End If
        Return Win32API.CallNextHookEx(mouseHookHandle, code, wparam, lparam)
    End Function
#End Region
End Class

Public Class FlatTextComboBox
    '''''''''''''''''''''''''''''''''''''
    ' This is the actual class to implement
    '''''''''''''''''''''''''''''''''''''
    Inherits FlatComboBase
    '// For use when hosted by a toolbar
    Public Sub New(ByVal toolBarUse As Boolean)
        '// Override parent, we don't want to do all the painting ourselves
        '// since we want to let the edit control deal with the text for editing
        '// the parent class ComboBoxBase knows to do the right stuff with 
        '// non-editable comboboxes as well as editable comboboxes as long
        '// as we change these flags below
        SetStyle(ControlStyles.AllPaintingInWmPaint Or ControlStyles.UserPaint Or ControlStyles.Opaque, False)
    End Sub

    Public Sub New()
        '// Override parent, we don't want to do all the painting ourselves
        '// since we want to let the edit control deal with the text for editing
        '// the parent class ComboBoxBase knows to do the right stuff with 
        '// non-editable comboboxes as well as editable comboboxes as long
        '// as we change these flags below
        SetStyle(ControlStyles.AllPaintingInWmPaint Or ControlStyles.UserPaint Or ControlStyles.Opaque, False)
    End Sub

    Protected Overrides Sub OnPaint(ByVal pe As PaintEventArgs)
        MyBase.OnPaint(pe)
    End Sub

    Protected Overrides Sub OnDrawItem(ByVal e As DrawItemEventArgs)
        '// Call base class to do the "Flat ComboBox" drawing
        MyBase.OnDrawItem(e)
        '// Draw text
        Dim g As Graphics = e.Graphics
        Dim bounds As Rectangle = e.Bounds
        Dim selected As Boolean = (e.State And DrawItemState.Selected) > 0
        Dim editSel As Boolean = (e.State And DrawItemState.ComboBoxEdit) > 0
        If e.Index <> -1 Then
            DrawComboBoxItem(g, bounds, e.Index, selected, editSel)
        End If
    End Sub

    Protected Overrides Sub DrawComboBoxItem(ByVal g As Graphics, ByVal bounds As Rectangle, ByVal Index As Integer, ByVal selected As Boolean, ByVal editSel As Boolean)
        '// Call base class to do the "Flat ComboBox" drawing
        MyBase.DrawComboBoxItem(g, bounds, Index, selected, editSel)
        If Index <> -1 Then
            Dim brush As SolidBrush
            If selected And editSel Then
                brush = New SolidBrush(SystemColors.MenuText)
            ElseIf (selected) Then
                brush = New SolidBrush(SystemColors.HighlightText)
            Else
                brush = New SolidBrush(SystemColors.MenuText)
            End If

            Dim textSize As Size = GetTextSize(g, Items(Index).ToString(), Font)
            Dim top As Integer = bounds.Top + (bounds.Height - textSize.Height) / 2
            g.DrawString(Items(Index).ToString(), Font, brush, New Drawing.PointF(bounds.Left + 1, top))
        End If
    End Sub

    Protected Overrides Sub DrawComboBoxItemEx(ByVal g As Graphics, ByVal bounds As Rectangle, ByVal Index As Integer, ByVal selected As Boolean, ByVal editSel As Boolean)
        '// This "hack" is necessary to avoid a clipping bug that comes from the fact that sometimes
        '// we are drawing using the Graphics object for the edit control in the combobox and sometimes
        '// we are using the graphics object for the combobox itself. If we use the same function to do our custom
        '// drawing it is hard to adjust for the clipping because of what was said about
        MyBase.DrawComboBoxItemEx(g, bounds, Index, selected, editSel)
        If Index <> -1 Then

            Dim brush As SolidBrush
            If selected And editSel Then
                brush = New SolidBrush(SystemColors.MenuText)
            ElseIf (selected) Then
                brush = New SolidBrush(SystemColors.HighlightText)
            Else
                brush = New SolidBrush(SystemColors.MenuText)

                Dim textSize As Size = GetTextSize(g, Items(Index).ToString(), Font)
                Dim top As Integer = bounds.Top + (bounds.Height - textSize.Height) / 2
                '// Clipping rectangle
                Dim clipRect As RectangleF = New RectangleF(bounds.Left + 4, top, bounds.Width - ARROW_WIDTH - 4, top + textSize.Height)
                g.DrawString(Items(Index).ToString(), Font, brush, clipRect)
            End If
        End If
    End Sub

    Public Shared Function GetTextSize(ByVal graphics As Graphics, ByVal text As String, ByVal font As Font) As Size
        Dim hdc As IntPtr = graphics.GetHdc()
        Dim fontHandle As IntPtr = font.ToHfont()
        Dim currentFontHandle As IntPtr = Win32API.SelectObject(hdc, fontHandle)

        Dim rect As Win32API.RECT = New Win32API.RECT()
        rect.left = 0
        rect.right = 0
        rect.top = 0
        rect.bottom = 0

        Win32API.DrawText(hdc, text, text.Length, rect, _
         (Win32API.DrawTextFormatFlags.DT_SINGLELINE Or Win32API.DrawTextFormatFlags.DT_LEFT Or Win32API.DrawTextFormatFlags.DT_CALCRECT))
        Win32API.SelectObject(hdc, currentFontHandle)
        Win32API.DeleteObject(fontHandle)
        graphics.ReleaseHdc(hdc)

        Return New Size(rect.right - rect.left, rect.bottom - rect.top)
    End Function
End Class

Friend Class ColorUtil
    '''''''''''''''''''''''''''''''''''''
    ' This is a helper class for converting
    ' color data around
    '''''''''''''''''''''''''''''''''''''
    Public Sub New()
        '// No need to construct this object
    End Sub

#Region " Members"
    '// Knowncolor names
    Public Shared KnownColorNames() As String = _
  {"Transparent", "Black", "DimGray", "Gray", "DarkGray", "Silver", "LightGray", "Gainsboro", "WhiteSmoke", "White", _
    "RosyBrown", "IndianRed", "Brown", "Firebrick", "LightCoral", "Maroon", "DarkRed", "Red", "Snow", "MistyRose", _
    "Salmon", "Tomato", "DarkSalmon", "Coral", "OrangeRed", "LightSalmon", "Sienna", "SeaShell", "Chocalate", _
    "SaddleBrown", "SandyBrown", "PeachPuff", "Peru", "Linen", "Bisque", "DarkOrange", "BurlyWood", "Tan", "AntiqueWhite", _
    "NavajoWhite", "BlanchedAlmond", "PapayaWhip", "Mocassin", "Orange", "Wheat", "OldLace", "FloralWhite", "DarkGoldenrod", _
    "Cornsilk", "Gold", "Khaki", "LemonChiffon", "PaleGoldenrod", "DarkKhaki", "Beige", "LightGoldenrod", "Olive", _
    "Yellow", "LightYellow", "Ivory", "OliveDrab", "YellowGreen", "DarkOliveGreen", "GreenYellow", "Chartreuse", "LawnGreen", _
    "DarkSeaGreen", "ForestGreen", "LimeGreen", "PaleGreen", "DarkGreen", "Green", "Lime", "Honeydew", "SeaGreen", "MediumSeaGreen", _
    "SpringGreen", "MintCream", "MediumSpringGreen", "MediumAquaMarine", "YellowAquaMarine", "Turquoise", "LightSeaGreen", _
    "MediumTurquoise", "DarkSlateGray", "PaleTurquoise", "Teal", "DarkCyan", "Aqua", "Cyan", "LightCyan", "Azure", "DarkTurquoise", _
    "CadetBlue", "PowderBlue", "LightBlue", "DeepSkyBlue", "SkyBlue", "LightSkyBlue", "SteelBlue", "AliceBlue", "DodgerBlue", _
    "SlateGray", "LightSlateGray", "LightSteelBlue", "CornflowerBlue", "RoyalBlue", "MidnightBlue", "Lavender", "Navy", _
    "DarkBlue", "MediumBlue", "Blue", "GhostWhite", "SlateBlue", "DarkSlateBlue", "MediumSlateBlue", "MediumPurple", _
    "BlueViolet", "Indigo", "DarkOrchid", "DarkViolet", "MediumOrchid", "Thistle", "Plum", "Violet", "Purple", "DarkMagenta", _
    "Magenta", "Fuchsia", "Orchid", "MediumVioletRed", "DeepPink", "HotPink", "LavenderBlush", "PaleVioletRed", "Crimson", _
    "Pink", "LightPink"}

    '// Systemcolors names
    Public Shared SystemColorNames() As String = { _
     "ActiveBorder", "ActiveCaption", "ActiveCaptionText", "AppWorkspace", "Control", "ControlDark", "ControlDarkDark", _
     "ControlLight", "ControlLightLight", "ControlText", "Desktop", "GrayText", "HighLight", "HighLightText", _
     "HotTrack", "InactiveBorder", "InactiveCaption", "InactiveCaptionText", "Info", "InfoText", "Menu", "MenuText", _
     "ScrollBar", "Window", "WindowFrame", "WindowText"}
#End Region

    '// Conversion between RGB and Hue, Saturation and Luminosity function helpers
    Public Shared Sub HSLToRGB(ByVal h As Single, ByVal s As Single, ByVal l As Single, ByRef r As Single, ByRef g As Single, ByRef b As Single)
        '// given h,s,l,[240 and r,g,b [0-255]
        '// convert h [0-360], s,l,r,g,b [0-1]
        h = (h / 240.0F) * 360.0F
        s /= 240
        l /= 240
        r /= 255
        g /= 255
        b /= 255

        '// Begin Foley
        Dim m1 As Single
        Dim m2 As Single

        '// Calc m2
        If l <= 0.5 Then
            '//m2=(l*(l+s)); seems to be typo in Foley??, replace l for 1
            m2 = (l * (1 + s))
        Else
            m2 = (l + s - l * s)
        End If

        '//calc m1
        m1 = 2.0F * l - m2

        '//calc r,g,b in [0-1]
        If s = 0 Then
            '	// Achromatic: There is no hue
            '	// leave out the UNDEFINED part, h will always have value
            r = g = b = l
        Else
            '	// Chromatic: There is a hue
            r = getRGBValue(m1, m2, h + 120.0F)
            g = getRGBValue(m1, m2, h)
            b = getRGBValue(m1, m2, h - 120.0F)
        End If

        '// End Foley
        '// convert to 0-255 ranges
        r *= 255.0F
        g *= 255.0F
        b *= 255.0F
    End Sub

    Private Shared Function getRGBValue(ByVal n1 As Single, ByVal n2 As Single, ByVal hue As Single) As Single
        '// Helper function for the HSLToRGB function above
        If hue > 360.0F Then
            hue -= 360.0F
        ElseIf hue < 0 Then
            hue += 360.0F
        End If

        If hue < 60.0F Then
            Return n1 + (n2 - n1) * hue / 60.0F
        ElseIf hue < 180.0F Then
            Return n2
        ElseIf hue < 240.0F Then
            Return n1 + (n2 - n1) * (240.0F - hue) / 60.0F
        Else
            Return n1
        End If
    End Function

    Public Shared Sub RGBToHSL(ByVal r As Integer, ByVal g As Integer, ByVal b As Integer, ByRef h As Single, ByRef s As Single, ByRef l As Single)

        '//Computer Graphics - Foley p.595
        Dim delta As Single
        Dim fr As Single = r / 255.0F
        Dim fg As Single = g / 255.0F
        Dim fb As Single = b / 255.0F
        Dim max As Single = Math.Max(fr, Math.Max(fg, fb))
        Dim min As Single = Math.Min(fr, Math.Min(fg, fb))

        '//calc the lightness
        l = (max + min) / 2

        If max = min Then
            '//should be undefined but this works for what we need
            s = 0.0F
            h = 240.0F
        Else
            delta = max - min
            '//calc the Saturation
            If l < 0.5F Then
                s = delta / (max + min)
            Else
                s = delta / (2.0F - (max + min))
            End If

            '//calc the hue
            If fr = max Then
                h = (fg - fb) / delta
            ElseIf fg = max Then
                h = 2.0F + (fb - fr) / delta
            ElseIf fb = max Then
                h = 4.0F + (fr - fg) / delta
            End If

            '//convert hue to degrees
            h *= 60.0F
            If h < 0.0F Then
                h += 360.0F
            End If
            '//end foley
        End If

        '//convert to 0-255 ranges
        '//h [0-360], h,l [0-1]
        l *= 240.0F
        s *= 240.0F
        h = (h / 360.0F) * 240.0F

    End Sub

    '// Visual Studio .NET colors calculation helpers
    Public Shared ReadOnly Property VSNetBackgroundColor() As Color
        Get
            Return CalculateColor(SystemColors.Window, SystemColors.Control, 220)
        End Get
    End Property

    Public Shared ReadOnly Property VSNetSelectionColor() As Color
        Get
            Return CalculateColor(SystemColors.Highlight, SystemColors.Window, 70)
        End Get
    End Property

    Public Shared ReadOnly Property VSNetStripeColor() As Color
        Get
            Return CalculateColor(SystemColors.Control, VSNetBackgroundColor, 195)
        End Get
    End Property

    Public Shared ReadOnly Property VSNetPressedColor() As Color
        Get
            Return CalculateColor(SystemColors.Highlight, ColorUtil.VSNetSelectionColor, 70)
        End Get
    End Property

    Public Shared ReadOnly Property VSNetToggleColor() As Color
        Get
            Return CalculateColor(SystemColors.Highlight, SystemColors.Window, 30)
        End Get
    End Property

    Private Shared Function CalculateColor(ByVal front As Color, ByVal back As Color, ByVal alpha As Integer) As Color
        '// Use alpha blending to brigthen the colors but don't use it
        '// directly. Instead derive an opaque color that we can use.
        '// -- if we use a color with alpha blending directly we won't be able 
        '// to paint over whatever color was in the background and there
        '// would be shadows of that color showing through
        Dim frontColor As Color = Color.FromArgb(255, front)
        Dim backColor As Color = Color.FromArgb(255, back)

        Dim frontRed As Single = frontColor.R
        Dim frontGreen As Single = frontColor.G
        Dim frontBlue As Single = frontColor.B
        Dim backRed As Single = backColor.R
        Dim backGreen As Single = backColor.G
        Dim backBlue As Single = backColor.B

        Dim fRed As Single = frontRed * alpha / 255 + backRed * Convert.ToSingle((255 - alpha) / 255)
        Dim newRed As Byte = Convert.ToByte(fRed)
        Dim fGreen As Single = frontGreen * alpha / 255 + backGreen * Convert.ToSingle((255 - alpha) / 255)
        Dim newGreen As Byte = Convert.ToByte(fGreen)
        Dim fBlue As Single = frontBlue * alpha / 255 + backBlue * Convert.ToSingle((255 - alpha) / 255)
        Dim newBlue As Byte = Convert.ToByte(fBlue)

        Return Color.FromArgb(255, newRed, newGreen, newBlue)
    End Function

    '// General functions
    Public Shared Function ColorFromPoint(ByVal g As Graphics, ByVal x As Integer, ByVal y As Integer) As Color
        Dim hDC As IntPtr = g.GetHdc()
        '// Get the color of the pixel first
        Dim colorref As UInt32 = Win32API.GetPixel(hDC, x, y)
        Dim Red As Byte = GetRValue(colorref)
        Dim Green As Byte = GetGValue(colorref)
        Dim Blue As Byte = GetBValue(colorref)
        g.ReleaseHdc(hDC)
        Return Color.FromArgb(Red, Green, Blue)
    End Function

    Public Shared Function IsKnownColor(ByVal color As Color, ByRef knownColor As Color, ByVal useTransparent As Boolean) As Boolean
        '// Using the Color structrure "FromKnowColor" does not work if 
        '// we did not create the color as a known color to begin with
        '// we need to compare the rgbs of both color 
        Dim currentColor As Color = color.Empty
        Dim badColor As Boolean = False
        Dim enumValue As KnownColor
        For enumValue = 0 To knownColor.YellowGreen.ToKnownColor
            currentColor = color.FromKnownColor(enumValue)
            Dim colorName As String = currentColor.Name
            If Not useTransparent Then
                badColor = (colorName = "Transparent")
                If (color.A = currentColor.A And color.R = currentColor.R And color.G = currentColor.G _
                 And color.B = currentColor.B And Not currentColor.IsSystemColor _
                 And Not badColor) Then
                    knownColor = currentColor
                    Return True
                End If
            End If
        Next enumValue
        Return False
    End Function

    Public Shared Function IsSystemColor(ByVal color As Color, ByRef knownColor As Color) As Boolean
        '// Using the Color structrure "FromKnowColor" does not work if 
        '// we did not create the color as a known color to begin with
        '// we need to compare the rgbs of both color 
        Dim currentColor As Color = color.Empty
        Dim enumValue As KnownColor
        For enumValue = 0 To knownColor.YellowGreen.ToKnownColor
            currentColor = color.FromKnownColor(enumValue)
            Dim colorName As String = currentColor.Name
            If (color.R = currentColor.R And color.G = currentColor.G _
                And color.B = currentColor.B And currentColor.IsSystemColor) Then
                knownColor = currentColor
                Return True
            End If
        Next enumValue

        Return False
    End Function

    Public Shared Function GetCOLORREF(ByVal color As Color) As UInt32
        Return RGB(color.R, color.G, color.B)
    End Function

    '// Windows RGB related macros

    Public Shared Function GetRValue(ByVal color As UInt32) As Byte
        Dim i As Integer = 0
        Dim iBinaryNumber As Integer = 0
        Dim retVal As Byte = 0
        For i = 0 To 7
            iBinaryNumber = (2 ^ i)
            If (Convert.ToInt32(color) And iBinaryNumber) = iBinaryNumber Then
                Dim iShiftedBinaryNumber As Byte = (2 ^ i)
                retVal = retVal Or iShiftedBinaryNumber
            End If
        Next
        Return retVal
    End Function

    Public Shared Function GetGValue(ByVal color As UInt32) As Byte
        Dim i As Integer = 0
        Dim iBinaryNumber As Integer = 0
        Dim retVal As Byte = 0
        For i = 8 To 15
            iBinaryNumber = (2 ^ i)
            If (Convert.ToInt32(color) And iBinaryNumber) = iBinaryNumber Then
                Dim iShiftedBinaryNumber As Byte = (2 ^ (i - 8))
                retVal = retVal Or iShiftedBinaryNumber
            End If
        Next
        Return retVal
    End Function

    Public Shared Function GetBValue(ByVal color As UInt32) As Byte
        Dim i As Integer = 0
        Dim iBinaryNumber As Integer = 0
        Dim retVal As Byte = 0
        For i = 16 To 23
            iBinaryNumber = (2 ^ i)
            If (Convert.ToInt32(color) And iBinaryNumber) = iBinaryNumber Then
                Dim iShiftedBinaryNumber As Byte = (2 ^ (i - 16))
                retVal = retVal Or iShiftedBinaryNumber
            End If
        Next
        Return retVal
    End Function

    Public Shared Function RGB(ByVal r As Integer, ByVal g As Integer, ByVal b As Integer) As UInt32
        Dim i As Integer = 0
        Dim iBinaryNumber As Integer = 0
        Dim retVal As Integer
        For i = 0 To 7
            iBinaryNumber = (2 ^ i)
            If (r And iBinaryNumber) = iBinaryNumber Then
                Dim iShiftedBinaryNumber As Byte = (2 ^ i)
                retVal = retVal Or iShiftedBinaryNumber
            End If
        Next
        iBinaryNumber = 0
        For i = 0 To 7
            iBinaryNumber = (2 ^ i)
            If (g And iBinaryNumber) = iBinaryNumber Then
                Dim iShiftedBinaryNumber As Integer = (2 ^ (i + 8))
                retVal = retVal Or iShiftedBinaryNumber
            End If
        Next
        iBinaryNumber = 0
        For i = 0 To 7
            iBinaryNumber = (2 ^ i)
            If (b And iBinaryNumber) = iBinaryNumber Then
                Dim iShiftedBinaryNumber As Integer = (2 ^ (i + 16))
                retVal = retVal Or iShiftedBinaryNumber
            End If
        Next
        Return Convert.ToUInt32(retVal)
    End Function
End Class

Friend Class EditCtrlHook
    '''''''''''''''''''''''''''''''''''''
    ' This class is used by the combobox classes
    ' to hook into some windows events.
    '''''''''''''''''''''''''''''''''''''
    Inherits System.Windows.Forms.NativeWindow
    Dim comboBox As FlatComboBase = Nothing
    Dim ignoreNextPaintMessage As Boolean = False

    Public Sub New(ByVal cb As FlatComboBase)
        comboBox = cb
    End Sub

    Protected Overrides Sub WndProc(ByRef m As Message)
        Dim currentMessage As Win32API.Msg = m.Msg

        Select Case m.Msg
            Case Win32API.Msg.WM_PAINT
                If ignoreNextPaintMessage Then
                    ignoreNextPaintMessage = False
                    Exit Sub
                End If

                If comboBox.forceUpdate And Not comboBox.DroppedDown Then
                    comboBox.forceUpdate = False
                    comboBox.ForceTheUpdate()
                End If

                If Not comboBox.Enabled Then
                    '// Let the edit control do its thing first
                    MyBase.WndProc(m)
                    '// This is going to generate another paint message
                    '// ignore it
                    ignoreNextPaintMessage = True
                    DrawDisableState()
                    Exit Sub
                End If
                Exit Select
            Case Win32API.Msg.WM_MOUSEMOVE
                RequestMouseLeaveMessage(m.HWnd)
                If Not comboBox.highlighted Then
                    Dim g As Graphics = Graphics.FromHwnd(comboBox.Handle)
                    comboBox.DrawComboBoxBorder(g, SystemColors.Highlight)
                    comboBox.DrawComboBoxArrowSelected(g, False)
                End If
                Exit Select
            Case Win32API.Msg.WM_MOUSELEAVE
                If comboBox.highlighted And Not comboBox.ContainsFocus Then
                    Dim g As Graphics = Graphics.FromHwnd(comboBox.Handle)
                    comboBox.DrawComboBoxBorder(g, SystemColors.Window)
                    comboBox.DrawComboBoxArrowNormal(g, False)
                End If
                Exit Select
            Case Else
                Exit Select
        End Select

        MyBase.WndProc(m)
    End Sub

    Private Sub DrawDisableState()
        '// we are just going to fill the edit area
        '// with a white background, the combobox 
        '// already does the hard part
        Dim g As Graphics = Graphics.FromHwnd(Handle)
        Dim rc As Win32API.RECT = New Win32API.RECT()
        Win32API.GetClientRect(Handle, rc)
        Dim clientSize As Rectangle = New Rectangle(rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top)
        g.FillRectangle(Brushes.White, clientSize)
    End Sub

    Private Sub RequestMouseLeaveMessage(ByVal hWnd As IntPtr)
        '// Crea the structure needed for WindowsAPI call
        Dim tme As Win32API.TRACKMOUSEEVENTS = New Win32API.TRACKMOUSEEVENTS()

        '// Fill in the structure
        tme.cbSize = Convert.ToUInt32(16)
        tme.dwFlags = Convert.ToUInt32(Win32API.TrackerEventFlags.TME_LEAVE)
        tme.hWnd = hWnd
        tme.dwHoverTime = Convert.ToUInt32(0)

        '// Request that a message gets sent when mouse leaves this window
        Win32API.TrackMouseEvent(tme)
    End Sub
End Class

Friend Class Win32API
    '''''''''''''''''''''''''''''''''''''
    ' this class is full of useful, re-usable
    ' VB ports of common Win32API stuff...
    '''''''''''''''''''''''''''''''''''''
    Public Delegate Function HookProc(ByVal nCode As Integer, ByVal wParam As IntPtr, ByVal lParam As IntPtr) As IntPtr

#Region " Enums and structs"
    Public Enum WindowsHookCodes
        WH_MSGFILTER = -1
        WH_JOURNALRECORD = 0
        WH_JOURNALPLAYBACK = 1
        WH_KEYBOARD = 2
        WH_GETMESSAGE = 3
        WH_CALLWNDPROC = 4
        WH_CBT = 5
        WH_SYSMSGFILTER = 6
        WH_MOUSE = 7
        WH_HARDWARE = 8
        WH_DEBUG = 9
        WH_SHELL = 10
        WH_FOREGROUNDIDLE = 11
        WH_CALLWNDPROCRET = 12
        WH_KEYBOARD_LL = 13
        WH_MOUSE_LL = 14
    End Enum

    Public Enum TrackerEventFlags As Integer
        TME_HOVER = &H1
        TME_LEAVE = &H2
        TME_QUERY = &H40000000
        TME_CANCEL = &H80000000
    End Enum

    Public Enum Msg
        WM_NULL = &H0
        WM_CREATE = &H1
        WM_DESTROY = &H2
        WM_MOVE = &H3
        WM_SIZE = &H5
        WM_ACTIVATE = &H6
        WM_SETFOCUS = &H7
        WM_KILLFOCUS = &H8
        WM_ENABLE = &HA
        WM_SETREDRAW = &HB
        WM_SETTEXT = &HC
        WM_GETTEXT = &HD
        WM_GETTEXTLENGTH = &HE
        WM_PAINT = &HF
        WM_CLOSE = &H10
        WM_QUERYENDSESSION = &H11
        WM_QUIT = &H12
        WM_QUERYOPEN = &H13
        WM_ERASEBKGND = &H14
        WM_SYSCOLORCHANGE = &H15
        WM_ENDSESSION = &H16
        WM_SHOWWINDOW = &H18
        WM_CTLCOLOR = &H19
        WM_WININICHANGE = &H1A
        WM_SETTINGCHANGE = &H1A
        WM_DEVMODECHANGE = &H1B
        WM_ACTIVATEAPP = &H1C
        WM_FONTCHANGE = &H1D
        WM_TIMECHANGE = &H1E
        WM_CANCELMODE = &H1F
        WM_SETCURSOR = &H20
        WM_MOUSEACTIVATE = &H21
        WM_CHILDACTIVATE = &H22
        WM_QUEUESYNC = &H23
        WM_GETMINMAXINFO = &H24
        WM_PAINTICON = &H26
        WM_ICONERASEBKGND = &H27
        WM_NEXTDLGCTL = &H28
        WM_SPOOLERSTATUS = &H2A
        WM_DRAWITEM = &H2B
        WM_MEASUREITEM = &H2C
        WM_DELETEITEM = &H2D
        WM_VKEYTOITEM = &H2E
        WM_CHARTOITEM = &H2F
        WM_SETFONT = &H30
        WM_GETFONT = &H31
        WM_SETHOTKEY = &H32
        WM_GETHOTKEY = &H33
        WM_QUERYDRAGICON = &H37
        WM_COMPAREITEM = &H39
        WM_GETOBJECT = &H3D
        WM_COMPACTING = &H41
        WM_COMMNOTIFY = &H44
        WM_WINDOWPOSCHANGING = &H46
        WM_WINDOWPOSCHANGED = &H47
        WM_POWER = &H48
        WM_COPYDATA = &H4A
        WM_CANCELJOURNAL = &H4B
        WM_NOTIFY = &H4E
        WM_INPUTLANGCHANGEREQUEST = &H50
        WM_INPUTLANGCHANGE = &H51
        WM_TCARD = &H52
        WM_HELP = &H53
        WM_USERCHANGED = &H54
        WM_NOTIFYFORMAT = &H55
        WM_CONTEXTMENU = &H7B
        WM_STYLECHANGING = &H7C
        WM_STYLECHANGED = &H7D
        WM_DISPLAYCHANGE = &H7E
        WM_GETICON = &H7F
        WM_SETICON = &H80
        WM_NCCREATE = &H81
        WM_NCDESTROY = &H82
        WM_NCCALCSIZE = &H83
        WM_NCHITTEST = &H84
        WM_NCPAINT = &H85
        WM_NCACTIVATE = &H86
        WM_GETDLGCODE = &H87
        WM_SYNCPAINT = &H88
        WM_NCMOUSEMOVE = &HA0
        WM_NCLBUTTONDOWN = &HA1
        WM_NCLBUTTONUP = &HA2
        WM_NCLBUTTONDBLCLK = &HA3
        WM_NCRBUTTONDOWN = &HA4
        WM_NCRBUTTONUP = &HA5
        WM_NCRBUTTONDBLCLK = &HA6
        WM_NCMBUTTONDOWN = &HA7
        WM_NCMBUTTONUP = &HA8
        WM_NCMBUTTONDBLCLK = &HA9
        WM_KEYDOWN = &H100
        WM_KEYUP = &H101
        WM_CHAR = &H102
        WM_DEADCHAR = &H103
        WM_SYSKEYDOWN = &H104
        WM_SYSKEYUP = &H105
        WM_SYSCHAR = &H106
        WM_SYSDEADCHAR = &H107
        WM_KEYLAST = &H108
        WM_IME_STARTCOMPOSITION = &H10D
        WM_IME_ENDCOMPOSITION = &H10E
        WM_IME_COMPOSITION = &H10F
        WM_IME_KEYLAST = &H10F
        WM_INITDIALOG = &H110
        WM_COMMAND = &H111
        WM_SYSCOMMAND = &H112
        WM_TIMER = &H113
        WM_HSCROLL = &H114
        WM_VSCROLL = &H115
        WM_INITMENU = &H116
        WM_INITMENUPOPUP = &H117
        WM_MENUSELECT = &H11F
        WM_MENUCHAR = &H120
        WM_ENTERIDLE = &H121
        WM_MENURBUTTONUP = &H122
        WM_MENUDRAG = &H123
        WM_MENUGETOBJECT = &H124
        WM_UNINITMENUPOPUP = &H125
        WM_MENUCOMMAND = &H126
        WM_CTLCOLORMSGBOX = &H132
        WM_CTLCOLOREDIT = &H133
        WM_CTLCOLORLISTBOX = &H134
        WM_CTLCOLORBTN = &H135
        WM_CTLCOLORDLG = &H136
        WM_CTLCOLORSCROLLBAR = &H137
        WM_CTLCOLORSTATIC = &H138
        WM_MOUSEMOVE = &H200
        WM_LBUTTONDOWN = &H201
        WM_LBUTTONUP = &H202
        WM_LBUTTONDBLCLK = &H203
        WM_RBUTTONDOWN = &H204
        WM_RBUTTONUP = &H205
        WM_RBUTTONDBLCLK = &H206
        WM_MBUTTONDOWN = &H207
        WM_MBUTTONUP = &H208
        WM_MBUTTONDBLCLK = &H209
        WM_MOUSEWHEEL = &H20A
        WM_PARENTNOTIFY = &H210
        WM_ENTERMENULOOP = &H211
        WM_EXITMENULOOP = &H212
        WM_NEXTMENU = &H213
        WM_SIZING = &H214
        WM_CAPTURECHANGED = &H215
        WM_MOVING = &H216
        WM_DEVICECHANGE = &H219
        WM_MDICREATE = &H220
        WM_MDIDESTROY = &H221
        WM_MDIACTIVATE = &H222
        WM_MDIRESTORE = &H223
        WM_MDINEXT = &H224
        WM_MDIMAXIMIZE = &H225
        WM_MDITILE = &H226
        WM_MDICASCADE = &H227
        WM_MDIICONARRANGE = &H228
        WM_MDIGETACTIVE = &H229
        WM_MDISETMENU = &H230
        WM_ENTERSIZEMOVE = &H231
        WM_EXITSIZEMOVE = &H232
        WM_DROPFILES = &H233
        WM_MDIREFRESHMENU = &H234
        WM_IME_SETCONTEXT = &H281
        WM_IME_NOTIFY = &H282
        WM_IME_CONTROL = &H283
        WM_IME_COMPOSITIONFULL = &H284
        WM_IME_SELECT = &H285
        WM_IME_CHAR = &H286
        WM_IME_REQUEST = &H288
        WM_IME_KEYDOWN = &H290
        WM_IME_KEYUP = &H291
        WM_MOUSEHOVER = &H2A1
        WM_MOUSELEAVE = &H2A3
        WM_CUT = &H300
        WM_COPY = &H301
        WM_PASTE = &H302
        WM_CLEAR = &H303
        WM_UNDO = &H304
        WM_RENDERFORMAT = &H305
        WM_RENDERALLFORMATS = &H306
        WM_DESTROYCLIPBOARD = &H307
        WM_DRAWCLIPBOARD = &H308
        WM_PAINTCLIPBOARD = &H309
        WM_VSCROLLCLIPBOARD = &H30A
        WM_SIZECLIPBOARD = &H30B
        WM_ASKCBFORMATNAME = &H30C
        WM_CHANGECBCHAIN = &H30D
        WM_HSCROLLCLIPBOARD = &H30E
        WM_QUERYNEWPALETTE = &H30F
        WM_PALETTEISCHANGING = &H310
        WM_PALETTECHANGED = &H311
        WM_HOTKEY = &H312
        WM_PRINT = &H317
        WM_PRINTCLIENT = &H318
        WM_HANDHELDFIRST = &H358
        WM_HANDHELDLAST = &H35F
        WM_AFXFIRST = &H360
        WM_AFXLAST = &H37F
        WM_PENWINFIRST = &H380
        WM_PENWINLAST = &H38F
        WM_APP = &H8000
        WM_USER = &H400
        WM_REFLECT = WM_USER + &H1C00
    End Enum

    Public Enum DrawTextFormatFlags As Integer
        DT_TOP = &H0
        DT_LEFT = &H0
        DT_CENTER = &H1
        DT_RIGHT = &H2
        DT_VCENTER = &H4
        DT_BOTTOM = &H8
        DT_WORDBREAK = &H10
        DT_SINGLELINE = &H20
        DT_EXPANDTABS = &H40
        DT_TABSTOP = &H80
        DT_NOCLIP = &H100
        DT_EXTERNALLEADING = &H200
        DT_CALCRECT = &H400
        DT_NOPREFIX = &H800
        DT_INTERNAL = &H1000
        DT_EDITCONTROL = &H2000
        DT_PATH_ELLIPSIS = &H4000
        DT_END_ELLIPSIS = &H8000
        DT_MODIFYSTRING = &H10000
        DT_RTLREADING = &H20000
        DT_WORD_ELLIPSIS = &H40000
    End Enum

    <StructLayout(LayoutKind.Sequential)> _
    Public Structure POINT
        Public x As Integer
        Public y As Integer
    End Structure

    <StructLayout(LayoutKind.Sequential)> _
    Public Structure RECT
        Public left As Integer
        Public top As Integer
        Public right As Integer
        Public bottom As Integer
    End Structure

    <StructLayout(LayoutKind.Sequential)> _
    Public Structure TRACKMOUSEEVENTS
        Public cbSize As UInt32
        Public dwFlags As UInt32
        Public hWnd As IntPtr
        Public dwHoverTime As UInt32
    End Structure

    Public Structure MOUSEHOOKSTRUCT
        Public pt As POINT
        Public hwnd As IntPtr
        Public wHitTestCode As Integer
        Public dwExtraInfo As IntPtr
    End Structure
#End Region

#Region " Methods"
#Region " Kernel32"
    <DllImport("kernel32.dll")> _
    Public Shared Function GetCurrentThreadId() As Integer
    End Function
#End Region

#Region " User32"
    <DllImport("user32.dll")> _
    Public Shared Function GetDlgItem(ByVal hDlg As IntPtr, ByVal nControlID As Integer) As IntPtr
    End Function
    <DllImport("user32.dll")> _
    Public Shared Function SetWindowsHookEx(ByVal hookid As Integer, ByVal pfnhook As HookProc, ByVal hinst As IntPtr, ByVal threadid As Integer) As IntPtr
    End Function
    <DllImport("user32.dll")> _
    Public Shared Function UnhookWindowsHookEx(ByVal hhook As IntPtr) As Boolean
    End Function
    <DllImport("user32.dll")> _
    Public Shared Function SetFocus(ByVal hWnd As IntPtr) As IntPtr
    End Function
    <DllImport("user32.dll")> _
    Public Shared Function CallNextHookEx(ByVal hhook As IntPtr, ByVal code As Integer, ByVal wparam As IntPtr, ByVal lparam As IntPtr) As IntPtr
    End Function
    <DllImport("User32.dll")> _
     Public Shared Function TrackMouseEvent(ByRef tme As TRACKMOUSEEVENTS) As Boolean
    End Function
    <DllImport("user32.dll")> _
    Public Shared Function GetClientRect(ByVal hWnd As IntPtr, ByRef rc As RECT) As Integer
    End Function
    <DllImport("user32.dll")> _
    Public Shared Function DrawText(ByVal hdc As IntPtr, ByVal lpString As String, ByVal nCount As Integer, ByRef lpRect As RECT, ByVal uFormat As Integer) As Integer
    End Function
#End Region

#Region " GDI32"
    <DllImport("gdi32.dll")> _
    Public Shared Function SelectObject(ByVal hDC As IntPtr, ByVal hObject As IntPtr) As IntPtr
    End Function
    <DllImport("gdi32.dll")> _
    Public Shared Function DeleteObject(ByVal hObject As IntPtr) As Boolean
    End Function
    <DllImport("gdi32.dll")> _
    Public Shared Function GetPixel(ByVal hDC As IntPtr, ByVal XPos As Integer, ByVal YPos As Integer) As UInt32
    End Function
#End Region
#End Region
End Class

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer
Malaysia Malaysia
As an experienced programmer, I know how hard it is to find good code sometimes. I hope my articles and samples are as good for you as they are for me. Need more good code? Email me.

Comments and Discussions