Click here to Skip to main content
Click here to Skip to main content

Making Standard ComboBox appear flat

By , 18 May 2005
 

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:

ComboBox  cmb = new ComboBox();

You will do:

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.

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

About the Author

Fadrian Sudaman
Architect SMS Management and Technology
Australia Australia
Member
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.

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralRe: Enable = falsememberjorge.ramos22 Nov '07 - 3:53 
Thanks for your answer!
With your code, the extra line disappears, but the controls draws in 3D.
I made some changes and now the combo is completly flat when it is disabled.
Here are the changes that I made...
 
case WM_PAINT:
...........
Pen p;
if (!this.Enabled)
BackColor = SystemColors.Control;
else
BackColor = SystemColors.Window;
p = new Pen(BackColor, 2);
gdc.DrawRectangle(p, new Rectangle(2, 2, this.Width - 3, this.Height - 3));
........
 
I'm under WinXP, but I would like to know if this solution works fine in vista too.
Thanks for your help!!
Regards,
 
Jorge
GeneralNeed DirectionmemberSE-Mrun20 Sep '07 - 18:59 
Hi
I did as u said in this Article.
FlatCombBox cmd = new FlatComboBox(); // On Page Load Event
Then..
cmd.Items.Add("A");
cmd.Items.Add("B");
cmd.Items.Add("C");
And on Button Click event
cmd.Show();
But i am unable to see the cmd ComboBox on my asp.net Page.
Is i am wrong any where....
Then please tell me.
 
Thanking you... Mrun Big Grin | :-D

 

 
Mrun

GeneralRe: Need DirectionmemberFadrian Sudaman20 Sep '07 - 21:46 
Hi Mrun,
This control is a WinForm control and not to be used in ASP.NET page.
 
Fadrian
GeneralRe: Need DirectionmemberSE-Mrun20 Sep '07 - 21:55 
Ok. Thanx
 
Mrun

GeneralQuestionmemberdavidbeseke16 Aug '07 - 11:42 
I have a need for a combo box control that will allow me to show but not select values from a data source that are marked inactive. For example, let's say I have a list of options A, B and C. I have some old records in my database that are linked to option B, but that option is no longer in use as is flagged inactive in the option table. I'd like the combo box to show the current value for the record as B, but if a user opens the value list they only see options A and C.
 
The way I was planning to do this is to somehow add another field name to the data binding so that the paint routine can tell which records are active and only show those when the list is opened. In other words, I'd have a DisplayMember, a ValueMember and an ActiveMember property for the combo box. If the field pointed to by the ActiveMember property is false for a record in the data source, that value doesn't show up in the list and cannot be selected by the user.
 
Can you give me some guidance on how I would alter the standard combo box to add that functionality?
GeneralRe: QuestionmemberFadrian Sudaman18 Aug '07 - 22:24 
Instead of customizing a combobox for the above scenario, the easiest way is just use normal combobox and use a few simple methods to achieve this:
- Populate the list: you will look at your data and only populate active entry (A, C) to the combo
- Set selection: if entry value is valid, then select. If the entry value not in the list, check if it is one of your inactive data (e.g. B), then insert that and select (select B).
- Write validator code to make sure your business rule are enforced on when inactive values can be used before saving the data.
 

GeneralRe: Questionmemberdavidbeseke19 Aug '07 - 4:16 
Thank you for your help. This solution is similar to the one I came up with. What I ended up doing is pulling in all the records for the dataset and using a dataview to populate the list. If the record I was looking at used an active list member, I set the RowFilter for populating the combo box to active = 1. If the row used an inactive member I set the RowFilter to active = 1 or id = the record key for the inactive record. That way the user could see the inactive value but they could only either keep it the same or change it to an active value from the combo box. Problem solved.
Questionwhy win32?memberjuras924 Jul '07 - 9:57 

protected override void WndProc(ref Message m)
{
if (this.DropDownStyle == ComboBoxStyle.Simple)
{
base.WndProc(ref m);
return;
}
 
IntPtr hDC = IntPtr.Zero;
Graphics gdc = this.CreateGraphics();
switch (m.Msg)
{
case WM_NC_PAINT:
base.WndProc(ref m);
PaintFlatControlBorder(this, gdc);
gdc.Dispose();
break;
case WM_PAINT:
base.WndProc(ref m);
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);
gdc.Dispose();
break;
default:
base.WndProc(ref m);
break;
}
}

 
I just hate .NET + win32 :>...
AnswerRe: why win32?memberFadrian Sudaman24 Jul '07 - 13:36 
Hi,
There is a slight difference between Graphics.CreateGraphics and Win32 Graphics.FromHdc using the Win32 device context. CreateGraphics give you the DC for the client area only, whereby FromHdc gives you the entire window area including non-client area. We need this to paint our border around the bound of the control. WM_NC_PAINT (Non Client area paint) is the windows event message sent specifically for paintint the non client area.
 
Hope the explaination above helps.
Fadrian
Generalgood job!memberapache1o5 Jun '07 - 12:24 
good job!

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

Permalink | Advertise | Privacy | Mobile
Web04 | 2.6.130523.1 | Last Updated 18 May 2005
Article Copyright 2004 by Fadrian Sudaman
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid