Flat Combobox Code

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • Don

    #1

    Flat Combobox Code

    I've looked high and low for some code that will allow me to have a combobox
    with a flat borderstyle. I found a few examples, but nothing that was
    really usable for me. I had the following criteria that I wanted to meet,
    but nothing did:

    - No reliance on a separate library
    - No unmanaged code
    - Easy subclassing of System.Windows. Forms.Combobox (i.e. no custom control
    monstrosity)
    - No extra funky features that I didn't need

    Some example had some very interesting features, like a multi-column
    drop-down list, but, in the end, they introduced needless clutter and
    complexity. So, I took one code example that I found and modified it so
    that it was as generic as possible. All it does is allow a subclassed
    combobox to have a flatstyle (along with the normal 3D style).

    To use this code, create a new class and post the following code into it. I
    tried to comment the code as much as possible, and tried to make it easy to
    read. My apologies for the long lines:


    --- CODE START ---


    Public Class ComboBoxEx
    Inherits System.Windows. Forms.ComboBox

    #Region " Control Flattening Code "

    #Region " Constants "

    Private Const DIZ_BackColor As String = "The background color of the
    control. Only applicable when BorderStyle is set to FixedSingle or None."
    Private Const DIZ_ButtonColor As String = "The color of the drop-down
    button on the combobox. Only applicable when BorderStyle is set to
    FixedSingle or None."
    Private Const DIZ_BorderColor As String = "The color of the combobox
    border. Only applicable when BorderStyle is set to FixedSingle."
    Private Const DIZ_BorderStyle As String = "The style of border for the
    combobox control."
    Private Const DIZ_ArrowColor As String = "The color of the arrow in the
    drop-down button. Only applicable when BorderStyle is set to FixedSingle or
    None."
    Private Const DIZ_FocusRectan gleColor As String = "The color of the
    focus rectangle. Only applicable when BorderStyle is set to FixedSingle or
    None."
    Private Const DIZ_DrawFocusRe ctangle As String = "Should a focus
    rectangle be drawn around the control? Only applicable when BorderStyle is
    set to FixedSingle or None."

    Private Const WM_PAINT As Integer = &HF

    #End Region
    #Region " Instance Variables "

    Private _BorderStyle As BorderStyle = BorderStyle.Fix ed3D
    Private _ButtonColor As Color = SystemColors.Co ntrol
    Private _BorderColor As Color = SystemColors.Wi ndowFrame
    Private _ArrowColor As Color = SystemColors.Co ntrolText
    Private _FocusRectangle Color As Color = SystemColors.Hi ghlight
    Private _DrawFocusRecta ngle As Boolean = False

    #End Region
    #Region " Instance Properties "

    ' NOTE: This method is only overridden to provide a new property
    designer description.
    '''<summary>The color of the combobox Back.</summary>
    '''<remarks>Onl y applicable when BorderStyle is set to FixedSingle or
    None.</remarks>
    <System.Compone ntModel.Descrip tion(DIZ_BackCo lor)> _
    Public Overrides Property BackColor() As Color
    Get
    Return MyBase.BackColo r
    End Get
    Set(ByVal Value As Color)
    MyBase.BackColo r = Value
    End Set
    End Property
    '''<summary>The color of the combobox button.</summary>
    '''<remarks>Onl y applicable when BorderStyle is set to FixedSingle or
    None.</remarks>
    <System.Compone ntModel.Descrip tion(DIZ_Button Color)> _
    Public Property ButtonColor() As Color
    Get
    Return _ButtonColor
    End Get
    Set(ByVal Value As Color)
    _ButtonColor = Value
    Me.Invalidate()
    End Set
    End Property
    '''<summary>The color of the combobox Border.</summary>
    '''<remarks>Onl y applicable when BorderStyle is set to
    FixedSingle.</remarks>
    <System.Compone ntModel.Descrip tion(DIZ_Border Color)> _
    Public Property BorderColor() As Color
    Get
    Return _BorderColor
    End Get
    Set(ByVal Value As Color)
    _BorderColor = Value
    Me.Invalidate()
    End Set
    End Property
    '''<summary>The combobox borderstyle.</summary>
    <System.Compone ntModel.Descrip tion(DIZ_Border Style)> _
    Public Property BorderStyle() As BorderStyle
    Get
    Return _BorderStyle
    End Get
    Set(ByVal Value As BorderStyle)
    _BorderStyle = Value
    Me.Invalidate()
    End Set
    End Property
    '''<summary>The color of the combobox Arrow.</summary>
    '''<remarks>Onl y applicable when BorderStyle is set to FixedSingle or
    None.</remarks>
    <System.Compone ntModel.Descrip tion(DIZ_ArrowC olor)> _
    Public Property ArrowColor() As Color
    Get
    Return _ArrowColor
    End Get
    Set(ByVal Value As Color)
    _ArrowColor = Value
    Me.Invalidate()
    End Set
    End Property
    '''<summary>The color of the combobox Highlight.</summary>
    '''<remarks>Onl y applicable when BorderStyle is set to FixedSingle or
    None.</remarks>
    <System.Compone ntModel.Descrip tion(DIZ_FocusR ectangleColor)> _
    Public Property FocusRectangleC olor() As Color
    Get
    Return _FocusRectangle Color
    End Get
    Set(ByVal Value As Color)
    _FocusRectangle Color = Value
    Me.Invalidate()
    End Set
    End Property
    '''<summary>Sho uld a highlight rectangle be drawn around the text area
    of the control? </summary>
    '''<remarks>Onl y applicable when BorderStyle is set to FixedSingle or
    None.</remarks>
    <System.Compone ntModel.Descrip tion(DIZ_DrawFo cusRectangle)> _
    Public Property DrawFocusRectan gle() As Boolean
    Get
    Return _DrawFocusRecta ngle
    End Get
    Set(ByVal Value As Boolean)
    _DrawFocusRecta ngle = Value
    End Set
    End Property

    #End Region
    #Region " Event Handlers & Overridden Methods "

    Protected Overrides Sub WndProc(ByRef m As Message)
    MyBase.WndProc( m)

    Select Case m.Msg

    Case WM_PAINT

    ' Draw the combobox
    DrawControl()

    End Select

    End Sub

    Private Sub MaximComboBox_G otFocus(ByVal sender As Object, ByVal e As
    System.EventArg s) Handles MyBase.GotFocus

    ' Needed for proper focus rectangle drawing
    Me.Invalidate()

    End Sub
    Private Sub MaximComboBox_L ostFocus(ByVal sender As Object, ByVal e As
    System.EventArg s) Handles MyBase.LostFocu s

    ' Needed for proper focus rectangle drawing
    Me.Invalidate()

    End Sub

    #End Region
    #Region " Instance Methods "

    '''<summary>Dra w the combobox control if the style is not 3D.</summary>
    Private Sub DrawControl()


    ' If we are not supposed to give the control 3D borders...
    ' NOTE: Since all the drawing that we're doing here is done overtop
    of the original control, not drawing anything will have
    ' the net result of the combobox appearing normally (i.e. with
    3D borders).
    If _BorderStyle <> BorderStyle.Fix ed3D Then

    Const BUTTON_WIDTH As Integer = 18 ' Width of the
    drop-down button. (Is this always 18?)
    Const DISABLED_ARROW_ DARKEN_AMOUNT As Byte = 32 ' how much
    should the drop-down arrow be darkened when the control is disabled?

    Dim g As Graphics = Me.CreateGraphi cs ' Graphics object to
    draw on
    Dim backcolorBrush As Brush ' Backcolor of the
    control
    Dim buttonX As Integer ' Drop-down button left
    coordinate
    Dim buttonY As Integer ' Drop-down button top
    coordinate
    Dim buttonWidth As Integer ' Drop-down button width
    Dim buttonHeight As Integer ' Drop-down button
    height
    Dim buttonRect As Rectangle ' Drop-down button area
    rectangle
    Dim dropButtonBrush As Brush ' Brush to draw
    drop-down button with
    Dim borderPen As Pen ' Pen to draw borders
    with
    Dim focusRectPen As Pen ' Pen to draw focus
    rectangle with
    Dim arrowPath As Drawing2D.Graph icsPath ' Path used to draw the
    drop-down button arrow
    Dim arrowTopLeft As PointF ' Top-left point for the
    drop-down button arrow
    Dim arrowTopRight As PointF ' Top-right point for
    the drop-down button arrow
    Dim arrowBottom As PointF ' Bottom point for the
    drop-down button arrow
    Dim arrowBrush As Brush ' Brush to draw the
    drop-down button arrow with


    ' Get a Graphics object for the control
    g = Me.CreateGraphi cs


    ' Get the background color (make it look greyed out if the
    control is disabled)
    If Me.Enabled Then
    backcolorBrush = New SolidBrush(Me.B ackColor)
    Else
    backcolorBrush = New SolidBrush(Syst emColors.Contro l)
    End If

    ' Fill in the background of the control
    g.FillRectangle (backcolorBrush , Me.ClientRectan gle)


    ' Draw the background of the dropdown button
    buttonX = Me.Width - BUTTON_WIDTH
    buttonY = 0
    buttonWidth = BUTTON_WIDTH
    buttonHeight = Me.Height
    buttonRect = New Rectangle(butto nX, buttonY, buttonWidth,
    buttonHeight)
    dropButtonBrush = New SolidBrush(_But tonColor)
    g.FillRectangle (dropButtonBrus h, buttonRect)


    ' If we are to draw a border...
    If _BorderStyle = BorderStyle.Fix edSingle Then

    ' Draw a border around the control and between the text area
    and the drop-down button
    borderPen = New Pen(_BorderColo r)
    With Me.ClientRectan gle
    g.DrawRectangle (borderPen, .X, .Y, .Width - 1, .Height -
    1) ' Border around control
    g.DrawLine(bord erPen, buttonX, buttonY, buttonX,
    buttonHeight) ' Line between text area and drop-down button
    End With

    End If


    ' If we are to draw a focus rectangle...
    If _DrawFocusRecta ngle And Me.Focused Then

    ' Draw a border around the text area of the control
    focusRectPen = New Pen(_FocusRecta ngleColor)

    With Me.ClientRectan gle
    If _BorderStyle = BorderStyle.Fix edSingle Then
    ' Note: We don't want to draw over the actual border
    of the control, so we will draw it inside by one pixel
    g.DrawRectangle (focusRectPen, .X + 1, .Y + 1,
    ..Width - 3, .Height - 3)
    ElseIf _BorderStyle = BorderStyle.Non e Then
    ' Note: Just like drawing a border!
    g.DrawRectangle (focusRectPen, .X, .Y, .Width - 1,
    ..Height - 1)
    End If
    End With

    End If


    ' Create the path for the arrow. This is basically an outline
    of the arrow's shape which we will "fill in"
    arrowPath = New Drawing2D.Graph icsPath
    arrowTopLeft = New PointF(Me.Width - 13, CSng((Me.Height - 5) /
    2)) ' TODO: This code should be cleaned
    arrowTopRight = New PointF(Me.Width - 6, CSng((Me.Height - 5) /
    2)) ' up so that it relies less on
    arrowBottom = New PointF(Me.Width - 9, CSng((Me.Height + 2) /
    2)) ' literals.
    arrowPath.AddLi ne(arrowTopLeft , arrowTopRight)
    arrowPath.AddLi ne(arrowTopRigh t, arrowBottom)

    ' Draw the arrow nice and smooth-like, see? Yeah...
    g.SmoothingMode = Drawing2D.Smoot hingMode.HighQu ality

    ' Get the arrow color (you might want to make it look greyed out
    if the control is disabled, though)
    arrowBrush = New SolidBrush(_Arr owColor)

    ' Draw the arrow
    g.FillPath(arro wBrush, arrowPath)


    End If


    End Sub

    #End Region

    #End Region

    End Class


    --- CODE END ---


    I have not extensively tested this, so if anyone finds any bugs or notices
    anything that could be improved, please feel free to post your thoughts. I
    just wanted to post this before I forgot so that others might benefit from
    it.

    - Don


  • Crouchie1998

    #2
    Re: Flat Combobox Code

    Hi Don,

    On http://www.codeproject.com are a few exellent exxamples of flat
    comboboxes

    Crouchie1998
    BA (HONS) MCP MCSE


    Comment

    • Don

      #3
      Re: Flat Combobox Code

      I've discovered that the selected text does not display when this combobox's
      DropDownStyle is set to DropDownList. To fix this, you need to add the
      following code:

      ' We only need to do this for DropDownLists
      If Me.DropDownStyl e = ComboBoxStyle.D ropDownList Then

      Dim x As Integer = 2
      Dim y As Integer = 4
      Dim textbrush As SolidBrush

      ' Create a brush to draw with
      textbrush = New SolidBrush(Me.F oreColor)

      ' Draw the text
      g.DrawString(Me .Text, Me.Font, textbrush, x, y)

      End If


      This should be placed after the following line of code in DrawControl:


      ' Draw the arrow
      g.FillPath(arro wBrush, arrowPath)


      Also, I've discovered that the combobox is really messed up when you set
      DropDownStyle to Simple. It'll take some extra work to fix that (which I
      don't think I'll bother with).

      - Don


      Comment

      Working...