Click here to Skip to main content
Click here to Skip to main content
Go to top

Flatten that Combobox!

, 2 Feb 2003
Rate this:
Please Sign up or sign in to vote.
A flat style combobox in VB.NET
<!-- Download Links -->

Flatten That Combobox

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. However, 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 data binding. The great thing is that it's flat.

The flat look is accomplished by subclassing, a cool OO concept that old VB developers new to VB.NET are just getting used to. There are 6 overridden methods:

    
    Protected Overrides Sub OnMouseEnter(ByVal e As System.EventArgs)
    Protected Overrides Sub OnMouseLeave(ByVal e As System.EventArgs)
    Protected Overrides Sub OnLostFocus(ByVal e As System.EventArgs)
    Protected Overrides Sub OnGotFocus(ByVal e As System.EventArgs)
    Protected Overrides Sub OnMouseHover(ByVal e As System.EventArgs)
    Protected Overrides Sub WndProc(ByRef m As Message)

The focus and enter methods all do pretty much the same thing. They call MyBase.OnMethod (which, amongst other things, is responsible for raising the proper events), set the color of the brush we will use to paint the background, and call Me.Invalidate(), which causes the control to repaint.

The real work is done by the WndProc method. For some reason that I have not figured out yet, the combobox does not call OnPaint in the base class. The control is actually drawn using windows messages. I used a Select Case to evaluate the message. Case &HF (15) is the value for the paint method. It comes from the windows message constant WM_PAINT.

Once we capture the WM_PAINT message, the work can begin. I simply color the background color according to the color that was set by one of the events mentioned earlier. (For example, OnGotFocus sets the color to SystemColors.Highlight, which is found in the System.Drawing namespace). I do this by creating a handle to a Graphics object and calling the FillRectangle method.

      Dim g As Graphics = Me.CreateGraphics
      g.FillRectangle(BorderBrush, Me.ClientRectangle)

The base class will draw an edit box on top of this background fill, giving a highlighted effect to the control. The next thing is to draw the rectangle for the dropdown button. This will always have a fixed width and location, relevant to the width of the combobox control. This is how the base control works, and it also makes locating the drawing area of the dropdown arrow much easier.

Dim rect As Rectangle = New Rectangle(Me.Width - 15, 3, 12, _
                                      Me.Height - 6)
      g.FillRectangle(DropButtonBrush, rect)

The last thing to do is to draw the dropdown arrow. This is done by creating a path and filling it. The Drawing2D.GraphicsPath object serves as a way to draw lines in a “freestyle” manner. These lines will always be connected. The path will always close itself in a straight line from the last mapped point to the first point. It’s kind of like drawing with an old etch-a-sketch where the pen never leaves the drawing surface. For this reason, to draw a triangle, it is only necessary to draw 2 lines.

What I did in the code below is declare the 3 points on the triangle (relative to the vertical middle of the control). Then I draw 2 lines. One line runs from top left to top right, and the other from the top right to the bottom.

 
 Dim pth As Drawing2D.GraphicsPath = New Drawing2D.GraphicsPath()
 Dim TopLeft As PointF = New PointF(Me.Width - 13, (Me.Height - 5) / 2)
 Dim TopRight As PointF = New PointF(Me.Width - 6, (Me.Height - 5) / 2)
 Dim Bottom As PointF = New PointF(Me.Width - 9, (Me.Height + 2) / 2)

 pth.AddLine(TopLeft, TopRight)
 pth.AddLine(TopRight, Bottom)

I then set the smoothing mode property of the graphics object so that the triangle will look crisper.

g.SmoothingMode = Drawing2D.SmoothingMode.HighQuality

Whenever we fill something, we need to use a Brush. The signature of the Fill methods ask for a Brush, but you cannot create an instance of Brush. The Brush class is abstract, meaning it must be inherited to be used. What to do? The best bet for a solid fill is to use the SolidBrush class. This can be tough to figure out, as it was for me when I was first starting with GDI+.

To determine the color to fill the triangle with, I check the DroppedDown property. I noticed on the combobox control, the arrow is 2 different colors, depending on if it is dropped down or not.

If Me.DroppedDown Then
    ArrowBrush = New SolidBrush(SystemColors.HighlightText)
Else
    ArrowBrush = New SolidBrush(SystemColors.ControlText)
End If

Finally, it is time to fill the triangle.

g.FillPath(ArrowBrush, pth)

Voila! There you have it. A flat combobox. I added a property to the control for button color, so the artsy GUI designers can have more flexibility. It’s code is straight forward for the most part, but I did want to point out something about the property set statement.

    Public Property ButtonColor() As Color
        Get
            Return _ButtonColor
        End Get
        Set(ByVal Value As Color)
            _ButtonColor = Value
            DropButtonBrush = New SolidBrush(Me.ButtonColor)
            Me.Invalidate()
        End Set
    End Property

Notice the call to Me.Invalidate(). This causes the control to repaint itself. Now, at design time, when the property is changed, the designer will show the new button color.

Short and sweet this project is. There are several good things to learn out of it though. Drawing custom controls using GDI+ and subclassing are 2 very important things to learn in .NET. I hope this article helps you to understand the breakdown of the code behind the FlatCombo, as well as helped to give you some more understanding about these important concepts.

I had previously done this project as a conversion from a C# project I found. If you download the source code, you will see the old version. It had a few quirky bugs around the way that it drew. It was also way overcomplicated. I thought I would leave it behind incase you wanted to see a different solution.

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

Share

About the Author

IbanezPlayer
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

 
QuestionThis is how i made thin border but It's flickering. Any help? Pinmemberelixirdagger230-Jan-13 13:22 
Protected Overrides Sub WndProc(ByRef m As Message)
        MyBase.WndProc(m)
 
        Select Case m.Msg
            Case &HF
                'Paint the background. Only the borders
                'will show up because the edit
                'box will be overlayed
                Dim g As Graphics = Me.CreateGraphics
                Dim p As Pen = New Pen(Color.White, 1)
                BorderBrush = New SolidBrush(Me.BackColor)
                g.FillRectangle(BorderBrush, Me.ClientRectangle)
                g.DrawRectangle(New Pen(Color.Blue, 1), Me.ClientRectangle.X, _
                Me.ClientRectangle.Y, Me.ClientRectangle.Width - 1, _
                Me.ClientRectangle.Height - 1)
 

                'Draw the background of the dropdown button
                Dim rect As Rectangle = New Rectangle(Me.Width - 15, 3, 12, Me.Height - 6)
                g.FillRectangle(DropButtonBrush, rect)
 
                'Create the path for the arrow
                Dim pth As Drawing2D.GraphicsPath = New Drawing2D.GraphicsPath()
                Dim TopLeft As PointF = New PointF(Me.Width - 13, (Me.Height - 5) / 2)
                Dim TopRight As PointF = New PointF(Me.Width - 6, (Me.Height - 5) / 2)
                Dim Bottom As PointF = New PointF(Me.Width - 9, (Me.Height + 2) / 2)
                pth.AddLine(TopLeft, TopRight)
                pth.AddLine(TopRight, Bottom)
 
                g.SmoothingMode = Drawing2D.SmoothingMode.HighQuality
 
                'Determine the arrow's color.
                If Me.DroppedDown Then
                    ArrowBrush = New SolidBrush(SystemColors.HighlightText)
                Else
                    ArrowBrush = New SolidBrush(SystemColors.ControlText)
                End If
 
                'Draw the arrow
                g.FillPath(ArrowBrush, pth)
 
            Case Else
                Exit Select
        End Select
    End Sub

GeneralDrawmode Pinmemberjeztericp16-Oct-09 8:11 
GeneralData access PinmemberAsad Abbasi4-Aug-05 10:10 
GeneralProblem When the DropDownStyle is changed to DropDownList PinmemberTweetyshwet23-Nov-04 17:57 
GeneralRe: Problem When the DropDownStyle is changed to DropDownList PinmemberAsad Abbasi4-Aug-05 10:04 
GeneralRe: Problem When the DropDownStyle is changed to DropDownList PinsussAsad Abbasi4-Aug-05 10:49 
GeneralReduce border size PinmemberDublinBoy14-Jun-04 23:44 
GeneralRe: Reduce border size PinsussAnonymous27-Jun-05 17:51 
GeneralIs there a way to do the same with DateTimePicker PinmemberDino687-Apr-04 10:09 
QuestionHow to set value to the index (not use the default index) PinmemberAishiteru12-Mar-04 0:28 
GeneralDateTimePicker Pinmembercaspian6-Feb-04 7:44 
GeneralRe: DateTimePicker PinmemberDino687-Apr-04 10:06 
GeneralRe: DateTimePicker PinmemberDino687-Apr-04 11:18 
GeneralRe: DateTimePicker PinmemberDino687-Apr-04 12:03 
GeneralRe: DateTimePicker Pinmembervachaun8-Sep-06 5:36 
GeneralFlatStyle property is there for code portability PinmemberTrevor Kennedy12-Jul-03 13:06 
GeneralEnabled Problem PinsussSDDev20-Jun-03 0:47 
GeneralRe: Enabled Problem PinmemberGuillermo Rivero12-Sep-03 9:08 
GeneralRe: Enabled Problem PinsussAnonymous12-Sep-03 14:53 
GeneralDropDownList PinmemberDerech29-Apr-03 11:30 
GeneralRe: DropDownList PinmemberNicholas Cardi24-Nov-03 6:32 
QuestionHow do you add the flatcombo to the controls toolbox? Pinmemberdomfos31-Mar-03 12:20 
AnswerRe: How do you add the flatcombo to the controls toolbox? PinmemberIbanezPlayer31-Mar-03 14:39 
Questionhow to bind data with textbox after executing search query thru code? Pinmemberdrmzunlimited25-Sep-02 12:10 
AnswerRe: how to bind data with textbox after executing search query thru code? PinsussAnonymous27-Dec-02 10:56 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web02 | 2.8.140922.1 | Last Updated 3 Feb 2003
Article Copyright 2002 by IbanezPlayer
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid