Click here to Skip to main content
12,303,794 members (71,790 online)
Click here to Skip to main content
Add your own
alternative version

Stats

187.5K views
486 downloads
87 bookmarked
Posted

Yet another DateTime...Slicker?

, 26 Nov 2003 MIT
Rate this:
Please Sign up or sign in to vote.
Everyone loves to hate the Windows Forms DateTimePicker control. This article shows how DateTimePicker can be improved without throwing the baby out with the bathwater. It describes bug fixes along with data-binding and usability enhancements to DateTimePicker.

Sample Image - DateTimeSlicker1.png

Introduction

I know, I know—you’re thinking, “Yet another DateTimePicker?” DateTimeSlicker is different and better. If you must, love it only for its oh-so-clever name.

With multiple DateTimePicker alternatives available, from .NET discussion group samples to $400 commercial controls, why would I spend time on this? My motivation—centered on improving upon the Windows Forms DateTimePicker—was threefold:

Others have reimplemented DateTimePicker, adding and removing features to produce a result that doesn’t appeal to me. DateTimePicker’s existing capabilities are many: empty values, keyboard and mouse input, culture sensitivity, XP (Luna) theming, and more. I didn’t want to take anything away; I just wanted to fix what’s broken.

Focus rectangle is drawn when it should not be

DateTimePicker paints a focus cue on its check box when the control is created, and any time the value of its Checked property changes. This means that a focus cue can appear while the control doesn’t actually have focus. This is unacceptable behavior in a well-mannered WinForms control! It’s confusing to have a control appear focused when it really isn’t.

DateTimePicker gets focus wrong

This bug has been with us since DateTimePicker was introduced years ago, and yet it doesn’t seem to get any airtime. I got tired of waiting for Redmond to fix the bug, so I decided to do something about it myself.

Lack of data-binding support for DBNull values

I hate how you can’t easily bind a DateTimePicker to a nullable ADO.NET DataColumn. Because a nullable column can contain a DBNull value, you can’t bind to DateTimePicker’s Text property nor to its Value property; these properties can accept only string values and DateTime values, respectively.

DateTimePicker can’t bind to DBNull values

You can set DateTimePicker’s Text property to null (Nothing in Visual Basic), but doing so doesn’t clear the check box; the current date and time are displayed instead. (I used Lutz Roeder’s .NET Reflector to verify this by decompiling the Windows Forms library. If you don’t have this terrific tool, download it now.)

The wonders of binding in Avalon are still months and months away, so I wanted these problems solved now in WinForms.

Unpopular display of empty values

DateTimePicker never appears empty, even when the check box is cleared. Instead, DateTimePicker’s Checked property—in addition to checking and clearing the check box—enables and disables (or “grays out”) the text. Many people despise this behavior. I myself am not fond of it because disabled text usually doesn’t look different enough from enabled text to avoid ambiguity, especially if your monitor’s contrast is not good. Users can easily mistake the disabled-text value in a DateTimePicker for an enabled-text value. It would be better to hide the text when the check box is cleared.

DateTimePicker can’t display empty values

Some have gone so far as to eliminate the check box from their DateTimePicker alternatives. I think this is a mistake, because in the effort to fix one usability problem they introduce others. How does the user clear a DateTimePicker if he can’t select all the text displayed in it? Do you really want to give up the behavior that allows only one element of the date/time to be selected at once? I toyed with the idea of replacing the check box with a delete button next to the dropdown button, but that didn’t feel right, either. Better to leave well enough alone, I think, and just try to hide the text when the check box is cleared.

Abortive attempts

Thomas Edison experimented with hundreds and hundreds of materials before he got his light bulb to work with a carbon filament. (The problem at hand is much simpler, of course, and so is the author.) In the effort to resolve all three of the aforementioned issues with DateTimePicker, I explored and abandoned multiple approaches that ranged from inheritance to containment in a UserControl to a combination of both. The root of the difficulty is that DateTimePicker’s Checked property is unreliable; in certain situations, it returns true while the check box appears to be cleared.

I tried listening to the Windows message stream (that is to say, I overrode DateTimePicker’s WndProc() method) in the attempt to keep track of the state of the check box, but the messages generated by a mouse click do not seem to correlate with the state of the check box. The message stream is exactly the same whether the check box is being checked or cleared.

I tried using reflection to monitor the state of private fields in DateTimePicker. There are two boolean fields that look promising (again, use .NET Reflector to decompile .NET executables). But neither of these, nor any other fields I could observe via reflection, reveals whether the check box is checked or cleared.

I also briefly considered writing my own DateTimePicker from scratch, but that was only a momentary lapse of reason. Losing type compatibility with the existing DateTimePicker would have been a disadvantage. More importantly, rewriting the DateTimePicker would have entailed too much work; I would have got mired in dark areas of Win32 development that I didn’t want to get into just now. Besides, it seems like overkill to reinvent the wheel when all that is needed is a little grease.

The implementation

I decided to handle the user input myself so that I would know for sure whether the check box was checked or cleared. I overrode WndProc() and handled the WM_CHAR and WM_LMOUSEDOWN messages. DateTimeSlicker hides the text by setting DateTimePicker’s Format and CustomFormat properties to show an empty string when the check box is cleared. Problem #3 solved.

DateTimeSlicker displays empty values

There are some legitimate uses for the controversial OOP feature of “shadowing”, or hiding, a base class member using the new modifier (the Shadows keyword in Visual Basic). DateTimeSlicker shadows DateTimePicker’s Value property so that it can accept either a DateTime value or a DBNull value. When the Value property is DBNull, DateTimeSlicker sets the Checked property to false. Problem #2 solved.

DateTimeSlicker binds to DBNull

Important note: Although shadowing does the job here, it only works properly if you call the new members from the interface in which they’re declared. For example, if I call the Checked, CustomFormat, Format and Value properties on a variable of type DateTimePicker, the new members will not be called, regardless of whether the object underlying my DateTimePicker variable is a DateTimeSlicker.

KlugeFocusBug() grabs focus and then focuses the previously active control again. This is admittedly not the most elegant solution (what workaround is?). But I was surprised to discover by experimentation that my managed-code approach is cleaner than posting Windows messages in order to manipulate focus. Problem #1 solved.

DateTimeSlicker gets focus right

Finally, because I interfered with some of DateTimePicker’s behavior, some events no longer fired as expected. I added code to suppress certain events and fire others.

The code

Here’s the nitty-gritty.

using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Security.Permissions;
using System.Windows.Forms;


namespace Umbrae.Windows.Forms
{
    // Author:     Nils Jonsson
    // Originated: 10/03/2003
    /// <summary>
    /// Represents an enhanced Windows date-time picker control.
    /// </summary>
    [DefaultEvent("ValueChanged")]
    [DefaultProperty("Value")]
    [ToolboxItemFilter("System.Windows.Forms")]
    public class DateTimeSlicker : DateTimePicker
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="DateTimePicker" />
        /// class.
        /// </summary>
        public DateTimeSlicker() : base()
        {
            this.ParentChanged += new EventHandler(this.this_ParentChanged);
            
            // Show the check box because it is most of the raison d’être for
            // this class.
            this.ShowCheckBox = true;
            
            this.customFormat = base.CustomFormat;
            this.format = base.Format;
        }
        
        
        /// <summary>
        /// Occurs when the value of the <see cref="Checked" /> property
        /// changes.
        /// </summary>
        [Category("Property Changed")]
        [Description("Occurs when the Checked property value is changed.")]
        public event EventHandler CheckedChanged
        {
            add { this.checkedChanged += value; }
            
            remove { this.checkedChanged -= value; }
        }
        
        /// <summary>
        /// Occurs when the value of the <see cref="CustomFormat" /> property
        /// changes.
        /// </summary>
        [Category("Property Changed")]
        [Description("Occurs when the CustomFormat property value is changed.")]
        public event EventHandler CustomFormatChanged
        {
            add { this.customFormatChanged += value; }
            
            remove { this.customFormatChanged -= value; }
        }
        
<A name=Checked>        /// <summary>
        /// Gets or sets a value indicating whether the <see cref="Value" />
        /// property has been set with a valid date-time value and the displayed
        /// value is able to be updated.
        /// </summary>
        /// <value><c>true</c> if the <see cref="Value" /> property has been set
        /// with a valid <see cref="DateTime" /> value and the displayed value
        /// is able to be updated; otherwise, <c>false</c>. The default is
        /// <c>true</c>.</value>
        [Bindable(true)]
        [Category("Behavior")]
        [DefaultValue(true)]
        [Description("Determines if the check box is checked, indicating that "
         + "the user has selected a value.")]
        public new bool Checked</A>

History

Monday, November 24th, 2003: Article first submitted.

Thursday, November 27th, 2003: Fixed a problem with dropping down and navigating the calendar via the keyboard (F4 or Alt+Down) when the check box is not checked. The control’s response to a drop-down request via the keyboard is now consistent with its response to a drop-down request via the mouse; the check box becomes checked if it is not checked, and the user must issue a second drop-down request in order to get the drop-down to occur. This behavior is not identical to that of DateTimePicker, but it’s close—I’ll get around to conforming it at some point.

License

This article, along with any associated source code and files, is licensed under The MIT License

Share

About the Author

Nils Jonsson
Architect NCite
United States United States
I’m part of a startup based in Houston, building products for law enforcement. I speak Ruby (since 2005), JavaScript (since 2005), and C# (since 2002).

You may also be interested in...

Comments and Discussions

 
QuestionPretty nice work Pin
gilchinger3-May-12 3:39
membergilchinger3-May-12 3:39 
GeneralProblem: Re-Activation the Dtp Pin
Schudi15-Feb-09 4:09
memberSchudi15-Feb-09 4:09 
GeneralRe: Problem: Re-Activation the Dtp Pin
Phillip Hustwick12-Dec-10 12:30
memberPhillip Hustwick12-Dec-10 12:30 
GeneralNot quite there Pin
dalb862310-Sep-08 5:59
memberdalb862310-Sep-08 5:59 
GeneralRe: Not quite there Pin
Patrick Fox20-Nov-08 6:26
memberPatrick Fox20-Nov-08 6:26 
GeneralSimpler example Pin
Adam Valpied29-Apr-08 3:25
memberAdam Valpied29-Apr-08 3:25 
GeneralRe: Simpler example Pin
Schudi15-Feb-09 0:14
memberSchudi15-Feb-09 0:14 
GeneralDataBinding: Date Text is not showed when the form is opened [modified] Pin
Marek Zgadzaj3-Feb-08 2:07
memberMarek Zgadzaj3-Feb-08 2:07 
AnswerRe: DataBinding: Date Text is not showed when the form is opened Pin
Marek Zgadzaj3-Feb-08 22:17
memberMarek Zgadzaj3-Feb-08 22:17 
GeneralRe: DataBinding: Date Text is not showed when the form is opened Pin
Bob Denny5-Oct-09 8:22
memberBob Denny5-Oct-09 8:22 
Questionregarding datetimepicker Pin
pasupulate10-Nov-07 0:29
memberpasupulate10-Nov-07 0:29 
GeneralDateTimeSlicker for VB6 Pin
oscar84831-May-07 6:28
memberoscar84831-May-07 6:28 
Generalthank you Nils Pin
rsam_london13-Sep-06 14:27
memberrsam_london13-Sep-06 14:27 
Generalunexpected behaviour Pin
rsam_london13-Sep-06 15:01
memberrsam_london13-Sep-06 15:01 
AnswerRe: unexpected behaviour Pin
Kwan Fu Sit5-Oct-06 15:26
memberKwan Fu Sit5-Oct-06 15:26 
GeneralFocus Bug Pin
piotrzarek2-May-06 5:01
memberpiotrzarek2-May-06 5:01 
Generalmessage 0x204E Pin
pASkuda1-Feb-05 4:45
memberpASkuda1-Feb-05 4:45 
GeneralRe: message 0x204E Pin
Phil J Pearson18-May-07 0:57
memberPhil J Pearson18-May-07 0:57 
AnswerRe: message 0x204E Pin
alleph15-Aug-07 15:04
memberalleph15-Aug-07 15:04 
GeneralRe: message 0x204E Pin
alleph15-Aug-07 18:00
memberalleph15-Aug-07 18:00 
GeneralKlugeFocusBug() very problematic Pin
Anonymous15-Oct-04 7:15
sussAnonymous15-Oct-04 7:15 
GeneralDataBinding fixed (2) and F4 Alt+Down fixed Pin
Mark Cranness19-Aug-04 4:17
sussMark Cranness19-Aug-04 4:17 
GeneralIssue with DataBindings.Add Pin
Elango Chidambaram15-Jul-04 10:14
memberElango Chidambaram15-Jul-04 10:14 
QuestionHow to let user modify value when binding to DBNull? Pin
CheowYong15-Jun-04 0:17
memberCheowYong15-Jun-04 0:17 
GeneralWorking alternative, Even for DataBinding Pin
koo91-Mar-04 7:04
memberkoo91-Mar-04 7: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.

| Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.160530.1 | Last Updated 27 Nov 2003
Article Copyright 2003 by Nils Jonsson
Everything else Copyright © CodeProject, 1999-2016
Layout: fixed | fluid