Click here to Skip to main content
15,887,214 members
Articles / Programming Languages / C#
Article

Making Standard ComboBox appear flat

Rate me:
Please Sign up or sign in to vote.
4.78/5 (45 votes)
18 May 20052 min read 382.8K   5.1K   74   76
A simple and easy class that draws the standard ComboBox as flat control.

Sample Image - flatcombo.jpg

Introduction

Many flat combobox controls out there are not based on the standard ComboBox control that is supplied by .NET, instead they have their own interfaces and requires a customized type of ComboBoxItem when inserting into the Items container. If you already have code written based on the standard .NET ComboBox, changing to the flat look may also require you to modify your codes. I have personally experienced that as a problem, and someone else has brought that up as a problem as well in my recent article DateTimePicker appears Flat. Many of the information provided here may appear in the DateTimePicker appears Flat article already, or maybe explained better in there, so please check it out.

Using the Class

This class inherits from ComboBox, therefore you can use it in exactly the same way as the ComboBox control or even as a replacement.

So instead of doing this:

C#
ComboBox  cmb = new ComboBox();

You will do:

C#
ComboBox cmb = new FlatComboBox();
// OR
FlatComboBox cmb = new FlatComboBox();

That is how simple it is :)

Code Explanation

To achieve the flat look for the control, I have to override the WndProc method, which is the method that processes through all the window messages for this control. We are particularly interested with WM_NC_PAINT and WM_PAINT messages.

C#
IntPtr hDC = GetWindowDC(this.Handle);

Graphics gdc = Graphics.FromHdc(hDC);

switch (m.Msg)

{
    case WM_NC_PAINT: 
        SendMessage(this.Handle, WM_ERASEBKGND, hDC, 0);
        SendPrintClientMsg(); // send to draw client area
        PaintFlatControlBorder(this, gdc);
        m.Result = (IntPtr) 1; // indicate msg has been processed 
        break;
    case WM_PAINT: 
        base.WndProc(ref m);
        // flatten the border area again
        Pen p = new Pen((this.Enabled? BackColor:SystemColors.Control), 2); 
        gdc.DrawRectangle(p, new Rectangle(2, 2, this.Width-3, this.Height-3));
        PaintFlatDropDown(this, gdc);
        PaintFlatControlBorder(this, gdc);
        break;
    default:
        base.WndProc(ref m);
        break;

}

ReleaseDC(m.HWnd, hDC);

gdc.Dispose(); 

}

WM_NC_PAINT message is received when the control needs to paint its border. Here I trapped the message and send WM_PRINTCLIENT message so that the ComboBox will draw its client area properly, then followed by drawing a flat border around it.

WM_PAINT message is received when the control needs to paint portion of its windows. ComboBox internally embeds a textbox and will draw the textbox with 3D border. A quick way to achieve the flat look is to paint a rectangle overlaying the 3D border of the textbox, therefore it will appear flat. We then paint the flat dropdown and border over it. Overriding the border is optional here, but I did it for the user experience where if the control is in focus, it will have a black line border or otherwise none.

Limitation

Currently, this FlatComboBox does not draw with ComboBox.Simple style. When this style is set for the ComboBox object, this class will let the base class perform the standard drawing.

History

  • 18 May 2005
    • DROPDOWNWIDTH value is dynamic now based on the system setting hence will work well in different screen resolutions.
    • Only acquires and creates window DC when necessary.

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
Architect SMS Management and Technology
Australia Australia
Fadrian Sudaman is an experienced IT professional who has worked with .NET technology since the early beta. His background stems from a strong C/C++ development experience in building large commercial applications and great appreciation for best practice and modern approaches for building quality software. Currently, Fadrian works as a senior consultant specialises in .NET technology involved in variety of roles including project management, solution architecture, presales and application development. Fadrian is also completing his PhD part time at Monash University, Australia.

Comments and Discussions

 
GeneralRe: Bug on 1900x1200 resolution Pin
Fadrian Sudaman18-May-05 17:51
Fadrian Sudaman18-May-05 17:51 
GeneralCool Combox Pin
samson197829-Sep-04 17:49
samson197829-Sep-04 17:49 
Generalconst int DROPDOWNWIDTH = 18; Pin
_DD_MORPH_4-Aug-04 22:13
_DD_MORPH_4-Aug-04 22:13 
GeneralRe: const int DROPDOWNWIDTH = 18; Pin
_DD_MORPH_4-Aug-04 23:07
_DD_MORPH_4-Aug-04 23:07 
GeneralRe: const int DROPDOWNWIDTH = 18; Pin
Fadrian Sudaman5-Aug-04 13:50
Fadrian Sudaman5-Aug-04 13:50 
QuestionHi Fadrian, can I use flat combo in VB.Net? Pin
KingVikram24-May-04 20:41
KingVikram24-May-04 20:41 
AnswerRe: Hi Fadrian, can I use flat combo in VB.Net? Pin
Fadrian Sudaman24-May-04 20:59
Fadrian Sudaman24-May-04 20:59 
GeneralRe: Hi Fadrian, can I use flat combo in VB.Net? Pin
Duc de Belfort28-Jun-04 0:34
Duc de Belfort28-Jun-04 0:34 
Hi all
Here it is, the basic flavour.
Still there are 3 questions open, as I did not understand quite well the C#-syntax. The code worked fine for me, but still it would be great if you could approve it Wink | ;-)


Duc_de_Belfort


'Questions: Are the translations correct??

'1. Question:
'if (ctrl.Focused == false || ctrl.Enabled == false )
'translated as:
' If (ctrl.Focused = False Or ctrl.Enabled = False) Then

'2. Question (what the hell is: this.enabled? BackColor: ... ????)
'Pen p = new Pen((this.Enabled? BackColor:SystemColors.Control), 2)
'translated as:
'Dim p As Pen = New Pen(Me.BackColor, 3)

'3. Question
'Public FlatComboBox()
' : base()
' {
' this.SetStyle(ControlStyles.DoubleBuffer, true);
' }
'translated as:
'Public Sub New()
' MyBase.New()
' Me.SetStyle(ControlStyles.DoubleBuffer, True)
'End Sub


Imports System
Imports System.Windows.Forms
Imports System.Drawing
Imports System.Drawing.Drawing2D
Imports System.Runtime.InteropServices


Public Class FlatComboBox

Inherits ComboBox

Public Const WM_ERASEBKGND As Integer = &H14
Public Const WM_PAINT As Integer = &HF
Public Const WM_NC_PAINT As Integer = &H85
Public Const WM_PRINTCLIENT As Integer = &H318

Declare Function SendMessage Lib "user32" Alias _
"SendMessageA" (ByVal hWnd As IntPtr, ByVal wMsg As Long, _
ByVal wParam As IntPtr, ByVal lParam As Object) As Long

Declare Function GetWindowDC Lib "user32" (ByVal hwnd As IntPtr) As IntPtr

Declare Function ReleaseDC Lib "user32" (ByVal hwnd As IntPtr, ByVal hDC As IntPtr) As Long


Public Sub New()
MyBase.New()
Me.SetStyle(ControlStyles.DoubleBuffer, True)
End Sub


Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)

If (Me.DropDownStyle = ComboBoxStyle.Simple) Then
MyBase.WndProc(m)

Else
Dim hdc As IntPtr = GetWindowDC(Me.Handle)
Dim gdc As Graphics = Graphics.FromHdc(hdc)

Select Case m.Msg
Case WM_NC_PAINT
SendMessage(Me.Handle, WM_ERASEBKGND, hdc, 0)
SendPrintClientMsg() ' send to draw client area
PaintFlatControlBorder(Me, gdc)
m.Result = New IntPtr(1) ' indicate msg has been processed

Case WM_PAINT
MyBase.WndProc(m)
' flatten the border area again
Dim p As Pen = New Pen(Me.BackColor, 3)
gdc.DrawRectangle(p, New Rectangle(2, 2, Me.Width - 3, Me.Height - 3))
PaintFlatDropDown(Me, gdc)
PaintFlatControlBorder(Me, gdc)

Case Else
MyBase.WndProc(m)

End Select

ReleaseDC(m.HWnd, hdc)
gdc.Dispose()

End If

End Sub


Private Sub SendPrintClientMsg()

' We send this message for the control to redraw the client area
Dim gClient As Graphics = Me.CreateGraphics()
Dim ptrClientDC As IntPtr = gClient.GetHdc()
SendMessage(Me.Handle, WM_PRINTCLIENT, ptrClientDC, 0)
gClient.ReleaseHdc(ptrClientDC)
gClient.Dispose()

End Sub

Private Sub PaintFlatControlBorder(ByVal ctrl As Control, ByVal g As Graphics)

Dim rect As Rectangle = New Rectangle(0, 0, ctrl.Width, ctrl.Height)
If (ctrl.Focused = False Or ctrl.Enabled = False) Then 'how is || transmitted: & or or?
ControlPaint.DrawBorder(g, rect, SystemColors.ControlDark, ButtonBorderStyle.Solid)
Else
ControlPaint.DrawBorder(g, rect, Color.Black, ButtonBorderStyle.Solid)
End If

End Sub

Public Sub PaintFlatDropDown(ByVal ctrl As Control, ByVal g As Graphics)
Const DROPDOWNWIDTH As Integer = 18
Dim rect As Rectangle = New Rectangle(ctrl.Width - DROPDOWNWIDTH, 0, DROPDOWNWIDTH, ctrl.Height)
ControlPaint.DrawComboButton(g, rect, ButtonState.Flat)
End Sub


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

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

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

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


End Class



GeneralRe: Hi Fadrian, can I use flat combo in VB.Net? Pin
Fadrian Sudaman1-Jul-04 16:19
Fadrian Sudaman1-Jul-04 16:19 
GeneralRe: Hi Fadrian, can I use flat combo in VB.Net? Pin
Duc de Belfort2-Jul-04 4:43
Duc de Belfort2-Jul-04 4:43 
GeneralRe: Hi Fadrian, can I use flat combo in VB.Net? Pin
Duc de Belfort2-Jul-04 4:45
Duc de Belfort2-Jul-04 4:45 
GeneralRe: Hi Fadrian, can I use flat combo in VB.Net? Pin
Elizabeth Gee13-Dec-04 6:27
Elizabeth Gee13-Dec-04 6:27 
GeneralRe: Hi Fadrian, can I use flat combo in VB.Net? Pin
Fadrian Sudaman13-Dec-04 11:14
Fadrian Sudaman13-Dec-04 11:14 
GeneralA little fix on the control Pin
Ricardo Mendes10-May-04 9:04
Ricardo Mendes10-May-04 9:04 

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

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