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
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
Comment