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.

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.

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.

Share

About the Author

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

Thanks for your input.
I haven't looked at the control for a while, so it is a slow start for me as well. These are my answers to your questions:
1. Instead of doing the drawing in mouseenter and mouseleave, I suggest that you just set a bool flag in those function such as m_bMouseEntered = true in the mouseenter even tna m_bMouseEntered = false in the mouseleave event. Change the implementation of PaintFlatControlBorder so that it first test for if (m_bMouseEntered) then call ControlPaint.DrawBorder(g, rect, MyColor, ButtonBorderStyle.Solid);

2. ComboBox has 3 style, simple style looks like a normal List control. you can set that property and have a look at your winform for the difference. I don't think the dropdownlist and dropdown style would be different in dealing with those events.

however, only messages WM_NC_PAINT and WM_PAINT make use of them, while for any other message they're not used at all, therefore, my advice to you would be to limit their scope to just the observed messages. In any case I know you're cleaning up:

ReleaseDC(m.HWnd, hDC);
gdc.Dispose();

however, why create and dispose of resources, in this case, relatively expensive resources, that will not be used.

Thanks. I agree with your point. The code was structured that way was partially caused by my laziness and also trying to make the code as easy to read as possible. I did plan to build up a library of these controls, but I have stopped for a while due to some change in circumstances. Keep your suggestions coming and will benefit me and others

Liz,
Geez.. that is a very wide screen that you have! I haven't got the environment to try, but did you try my suggested code in other thread:

====================================================
When it come to adjusting for drawing, I tempt to use the graphic resolution, so say "g" is the Graphics object, I would do something like this:
int iWidth = (int) ((g.DpiX/96.0f) * DROPDOWNWIDTH);

I use 96 there because that is the pixel size for standard font display. I also make it a float so that we keep the precision when performing the math.
====================================================

I am new to CodeProject.com and am excited to hear from you. Yes I did add this dropdownwidth code snippet from ont of the threads as well as the resize snippet.

I also added the OnResize snippet

Everything compiles well.

I think it may be the videocard on the Sony Vaio stretches things, as I am having troubles with the rest of my form where relative co-ordinates are working out better than absolute co-ordinates. This is interesting, and may become more of an issue for software developers as the panoramic screen resolutions become more common over time.

Liz,
AutoScale can be nasty and I constantly run into problem with it.
Can you capture the screen of the 3D wide artifact problem and send to my email a/c? Once I see the exact prob, I may be able to make other suggestion.

Liz,
Looks like the width of the dropdown combo is 1 pixel out. I would just change the DROPDOWNWIDTH to 19. It doesn't really matter in normal screen display for the dropdown combo to be 1 pixel wider. Perhaps 19 would be a better size to be used as it will cover the standard 17 pixel width combo and total of 2 3D pixels outer border for the left and right.

Glad to hear. Hmm... the solution may only work with high res display, and may not view nicely in low res display. I still suspect the problem lies with the AutoScale setting and the font used, but don't take my word for that. I yet to find a programatic way to solve this problem. If anyone knows the answer, I will appreciate your feedback.

I tested this on my Sony Vaio at:1920x1200, 1600x1200, and 1280x1024. At the higher two resolutions the below fix works great.

Also on my standard Dell desktop machine at 1280x1024, it works great.

I added a check to your code to check for the Screen Resolution width, and it adjusts the DROPDOWNWIDTH for the resolution above or below 1500.

I compiled this C# into a DLL and then tested it on both machines.
What I ran into was the Sony Vaio does a software screen resolution change, not a hardware one, thus when I set the Sony Vaio to 1280x1024, get that small vertical artifact between the textbox and the arrow parts of the combo box.

All the rest of the code I left the same as the last time I posted it on this thread. I included the VB.Net and C#.Net variations of this code snippet below...
[Visual Basic.Net]
Public Sub PaintFlatDropDown(ByVal ctrl As Control, ByVal g As Graphics)
Dim DROPDOWNWIDTH As Integer
Dim scrRes As Screen
Dim intScrWidth As Integer
intScrWidth = scrRes.PrimaryScreen.Bounds.Width

I've just found that my code snippet shows part of the 3D button aswel, but not due to the resolution but due to the OS it's running. On XP it runs perfectlly and it adjusts to the button width, but the 3D presentation of the button on Windows 2000, is a bit different snd it messes up a bit.

To correct this problem and hide the 3D section of the button (Same as your posted image) I did the following extra modification:

As you can see I've added the 3D border default width, this hides completelly the button... I've used the variable "myWidth" as you should add a check in order to add the border Width if windows themes are disabled, and if they are enabled we just skip this check.

Best wishes and hope you find it usefull.

And just a question: You're running the control on a Pre-Windows XP, right?

Hi,
All the code logic will be the same, it is just the mapping of C# code to VB.NET flavour. Unfortunately, I don't have any VB.NET class handy for this control, so you may have to write one yourself. Alternatively, you can make this library and used it in your VB.NET project.

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

' 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()

Sorry for the late reply.
OK, answer to your questions in order:
1. Correct
2. Conditional Statement. It is like a shorthand to if else statement. You will write in VB as
If (Enabled = True) Then
pen = new Pen(Me.BackColor, 2)
Else
pen = new Pen(SystemColors.Control, 2)
EndIf
3. Correct