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

DateTimePicker appears flat

Rate me:
Please Sign up or sign in to vote.
3.40/5 (25 votes)
17 May 20053 min read 253.2K   2.8K   43   61
A simple and easy class that draws the standard DateTimePicker as a flat control.

Sample Image - FlatDateTimePicker.jpg

Introduction

All the while, we have been using Lumisoft flat controls for our project. However the date picker control supplied does not have support for the time element. I decided to use the standard .NET DateTimePicker control, but the 3D appearance of the control looked really awful in the middle of all the flat controls. After much searching and reading, I couldn't find a readily usable class/control for the purpose that I wanted, and some suggested codes were rather complicated. Based on these information and suggestions by other authors, I came out with this class which is simple and yet functional. It does exactly what I want in a very simple way as well, by using the ControlPaint class supplied in the .NET framework.

Using the Class

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

So instead of doing this:

C#
DateTimePicker dtp = new DateTimePicker();

You will do:

C#
DateTimePicker dtp = new FlatDateTimePicker();

// OR

FlatDateTimerPicker dtp = new FlatDateTimePicker();

That is how simple it is :)

Code Explanation

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

Note that in this method, we need to acquire the actual window DC for the control using WinAPI GetWindowDC and release it later by using ReleaseDC. One main reason of using this over simply creating the Graphics object is because we need the entire DC of the control (bound rectangle) not just the client rectangle, therefore we can paint the border.

C#
protected override void WndProc(ref Message m)
{
 IntPtr hDC = GetWindowDC(m.HWnd);
 Graphics gdc = Graphics.FromHdc(hDC);
 switch (m.Msg)
 {
  case WM_NC_PAINT:
   SendMessage(this.Handle, WM_ERASEBKGND, hDC, 0);
   SendPrintClientMsg();
   SendMessage(this.Handle, WM_PAINT, IntPtr.Zero, 0);
   OverrideControlBorder(gdc);

   m.Result = (IntPtr)1; // indicate msg has been processed
   break;
  case WM_PAINT: base.WndProc(ref m);
   OverrideControlBorder(gdc);
   OverrideDropDown(gdc);
   break;
    case  WM_NC_HITTEST:
   base.WndProc(ref m);
   if (DroppedDown)
    this.Invalidate(this.ClientRectangle, false);
   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 do the border painting myself without calling the base class method, because the base class will try to draw this with a 3D border. I also clear the area prior to the border drawing otherwise it may cause some potential redraw problem when a control/dialog overlays on top such as when a combo box pops up.

WM_PAINT message is received when the control needs to paint portions of its windows. Here we just pass the message to the base class and let the control take care of the drawing (including the display value), but after, we will override the border and the drop down button to ensure that they are flat. 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.

WM_NCHITTEST message is received when you move your mouse around within the control. Many have attempted to write flat controls but were unable to draw the pulldown arrow in flat when the popup appears, because of not trapping this message properly. Every time the pulldown is clicked, the CloseUp or DropDown is called and followed by a redraw which will paint the 3D arrow onto the control; therefore to achieve the flat look, we have to redraw it here. Simply calling Invalidate in this case block will cause flickering, so we will only want to redraw it when the Calendar control actually pops up.

Change Logs

  • 13 April 2004
    • Posted.
  • 22 April 2004
    • Fixed the missing text problem reported.
  • 18 May 2005
    • Fixed flickering problem.
    • DROPDOWNWIDTH value is dynamic now based on the system setting, hence will work well in different screen resolutions.
    • Fixed drop down button redraw problem when mouse is not moved.

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

 
Questionflat drop-down arrow button? Pin
Kaine2-Aug-10 0:46
Kaine2-Aug-10 0:46 
GeneralDropdown Icon Pin
Dewayne Dodd6-Apr-09 15:02
Dewayne Dodd6-Apr-09 15:02 
Generalfine control with one question Pin
thirstyDev2-Dec-08 19:56
thirstyDev2-Dec-08 19:56 
GeneralRe: fine control with one question Pin
Fadrian Sudaman3-Dec-08 2:10
Fadrian Sudaman3-Dec-08 2:10 
GeneralGood work!! Just a small correction on PInvoke declaration Pin
C. Augusto Proiete11-Jul-08 5:27
C. Augusto Proiete11-Jul-08 5:27 
GeneralRed Rectangle Pin
diazpablomiguel23-Apr-08 5:22
diazpablomiguel23-Apr-08 5:22 
GeneralVB.net version Pin
MSP129-May-07 22:20
MSP129-May-07 22:20 
GeneralRe: VB.net version Pin
MSP129-May-07 22:35
MSP129-May-07 22:35 
GeneralRe: VB.net version Pin
Sebastian Br.22-Feb-10 1:20
Sebastian Br.22-Feb-10 1:20 
GeneralDateTimePicker with Popup FlatStyle support Pin
vachaun4-May-07 10:30
vachaun4-May-07 10:30 
GeneralRe: DateTimePicker with Popup FlatStyle support Pin
Fadrian Sudaman5-May-07 15:00
Fadrian Sudaman5-May-07 15:00 
GeneralRe: DateTimePicker with Popup FlatStyle support [modified] Pin
vachaun6-May-07 10:33
vachaun6-May-07 10:33 
Here is the Win32.cs file contents. It only consists of API information:

using System;<br />
using System.Collections.Generic;<br />
using System.Text;<br />
using System.Runtime.InteropServices;<br />
<br />
namespace ExtendedControls<br />
{<br />
    internal class Win32<br />
    {<br />
        #region Windows Constants<br />
        // NonClient messages<br />
        public const int WM_NCCALCSIZE = 131;<br />
        public const int WM_NCHITTEST = 132;<br />
        public const int WM_NCPAINT = 133;<br />
        public const int WM_NCMOUSEMOVE = 160;<br />
<br />
        // Client messages<br />
        public const int WM_SETFOCUS = 7;<br />
        public const int WM_KILLFOCUS = 8;<br />
        public const int WM_PAINT = 15;<br />
        public const int WM_ERASEBKGND = 14;<br />
        public const int WM_SETCURSOR = 32;<br />
        public const int WM_MOUSEMOVE = 512;<br />
        public const int WM_LBUTTONDOWN = 513;<br />
        public const int WM_LBUTTONUP = 514;<br />
        public const int WM_MOUSELEAVE = 675;<br />
        public const int WM_PRINTCLIENT = 792;<br />
        public const int CB_GETCOMBOBOXINFO = 356;<br />
<br />
        // Window Style constants<br />
        public const int GWL_STYLE = -16;<br />
        public const int GWL_EXSTYLE = -20;<br />
        public const int ECM_FIRST = 0x1500;<br />
        public const int EM_SETCUEBANNER = (ECM_FIRST + 1);<br />
        #endregion<br />
<br />
        #region Windows Structures<br />
        #region RECT Structure<br />
        [StructLayout(LayoutKind.Sequential)]<br />
        public struct RECT<br />
        {<br />
            public int Left;<br />
            public int Top;<br />
            public int Right;<br />
            public int Bottom;<br />
        }<br />
        #endregion<br />
<br />
        #region NCCALCSIZE_PARAMS Structure<br />
        [StructLayout(LayoutKind.Sequential)]<br />
        public struct NCCALCSIZE_PARAMS<br />
        {<br />
            public RECT rgrc0, rgrc1, rgrc2;<br />
            public IntPtr lppos;<br />
        }<br />
        #endregion<br />
<br />
        #region COMBOBOXINFO Structure<br />
        [StructLayout(LayoutKind.Sequential)]<br />
        public struct COMBOBOXINFO<br />
        {<br />
            public int cbSize;<br />
            public RECT rcItem;<br />
            public RECT rcButton;<br />
            public IntPtr stateButton;<br />
            public IntPtr hwndCombo;<br />
            public IntPtr hwndEdit;<br />
            public IntPtr hwndList;<br />
        }<br />
        #endregion<br />
<br />
        #region DATETIMEPICKERINFO Structure<br />
        [StructLayout(LayoutKind.Sequential)]<br />
        public struct DATETIMEPICKERINFO<br />
        {<br />
            public int cbSize;                  // Marshal.SizeOf(DATETIMEPICKERINFO)<br />
            public RECT rcCheck;                // Rect structure of checkbox if any<br />
            public int stateCheck;              // State of rcCheck STATE_SYSTEM_CHECKED or STATE_SYSTEM_INVISIBLE<br />
            public RECT rcButton;               // Rect structure of dropdown button or up/down control<br />
            public int stateButton;             // State rcButton one or more of STATE_SYSTEM_UNAVAILABLE, STATE_SYSTEM_INVISIBLE, or STATE_SYSTEM_PRESSED (invisible if up/down in use)<br />
            public IntPtr hwndEdit;             // Handle to edit control<br />
            public IntPtr hwndUD;               // Handle to up/down control<br />
            public IntPtr hwndDropDown;         // Handle to grid drop down<br />
        }<br />
        #endregion<br />
<br />
        #endregion<br />
<br />
        #region Constructor<br />
        public Win32()<br />
        {<br />
            //<br />
            // Constructor<br />
            //<br />
        }<br />
        #endregion<br />
<br />
        #region Windows API<br />
        [DllImport("user32", CharSet = CharSet.Auto)]<br />
        public static extern int GetWindowLong(IntPtr hWnd, int index);<br />
<br />
        [DllImport("user32", CharSet = CharSet.Auto)]<br />
        public static extern int SetWindowLong(IntPtr hWnd, int index, int value);<br />
<br />
        [DllImport("user32", CharSet = CharSet.Unicode)]<br />
        public static extern bool SendMessage(IntPtr hWnd, int wMsg, IntPtr wParam, string lParam);<br />
<br />
        [DllImport("user32", CharSet = CharSet.Auto)]<br />
        public static extern bool SendMessage(IntPtr hWnd, int wMsg, IntPtr wParam, ref COMBOBOXINFO lParam);<br />
<br />
        [DllImport("user32")]<br />
        public static extern IntPtr GetWindowDC(IntPtr hWnd);<br />
<br />
        [DllImport("user32")]<br />
        public static extern int ReleaseDC(IntPtr hWnd, IntPtr hDC);<br />
<br />
        [DllImport("user32", CharSet = CharSet.Auto)]<br />
        public static extern int SendMessage(IntPtr hWnd, int wMsg, IntPtr wParam, int lParam);<br />
<br />
        [DllImport("user32")]<br />
        public static extern bool GetComboBoxInfo(IntPtr hwndCombo, ref COMBOBOXINFO info);<br />
        #endregion<br />
<br />
        #region Utility Functions<br />
        /// <summary><br />
        /// Retrieves the highorder word of a value<br />
        /// </summary><br />
        /// <param name="param"></param><br />
        /// <returns></returns><br />
        public static int HiWord(int param)<br />
        {<br />
            if ((param & 0x80000000) == 0x80000000)<br />
                return (param >> 16);<br />
            else<br />
                return (param >> 16) & 0xffff;<br />
        }<br />
<br />
        /// <summary><br />
        /// Retrieves the loworder word of a value<br />
        /// </summary><br />
        /// <param name="param"></param><br />
        /// <returns></returns><br />
        public static int LoWord(int param)<br />
        {<br />
            return param & 0xffff;<br />
        }<br />
        #endregion<br />
    }<br />
}<br />



-- modified at 11:35 Monday 7th May, 2007

There is also a strange bug that I cannot seem to pinpoint. In Popup style, when you mouse out of the right hand side of the control slowly, the border on the button does not redraw. Or, if it does redraw, the code is coming up with a result that the mouse is still in the control. If you drag out at a normal or fast speed, then the button border draws correctly. It's only one the right hand side though.
GeneralRe: DateTimePicker with Popup FlatStyle support Pin
Fadrian Sudaman9-May-07 2:57
Fadrian Sudaman9-May-07 2:57 
GeneralRe: DateTimePicker with Popup FlatStyle support Pin
vachaun10-May-07 5:14
vachaun10-May-07 5:14 
GeneralRe: DateTimePicker with Popup FlatStyle support Pin
Fadrian Sudaman10-May-07 13:22
Fadrian Sudaman10-May-07 13:22 
GeneralRe: DateTimePicker with Popup FlatStyle support Pin
vachaun11-May-07 2:25
vachaun11-May-07 2:25 
QuestionVB.net version Pin
rgf2119-Mar-07 1:55
rgf2119-Mar-07 1:55 
AnswerRe: VB.net version Pin
Fadrian Sudaman9-May-07 3:01
Fadrian Sudaman9-May-07 3:01 
GeneralRe: VB.net version Pin
MSP129-May-07 22:40
MSP129-May-07 22:40 
QuestionPInvokeStackInbalanceException raised Pin
Jacek Osiecki16-Jan-07 2:12
Jacek Osiecki16-Jan-07 2:12 
AnswerRe: PInvokeStackInbalanceException raised Pin
Fadrian Sudaman16-Jan-07 13:00
Fadrian Sudaman16-Jan-07 13:00 
GeneralRe: PInvokeStackInbalanceException raised Pin
Jacek Osiecki16-Jan-07 23:38
Jacek Osiecki16-Jan-07 23:38 
GeneralTransparencyKey problem Pin
AlexDias3-Oct-06 8:12
AlexDias3-Oct-06 8:12 
GeneralRe: TransparencyKey problem Pin
Fadrian Sudaman4-Oct-06 3:25
Fadrian Sudaman4-Oct-06 3:25 
GeneralTime select didn not appear Pin
Member 144996413-Jun-05 8:12
Member 144996413-Jun-05 8:12 

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.