DateTimePicker
The DateTimePicker control lets the user interactively pick a single date and time. It provides the developer lot of flexibility and assurance that any value it returns is a valid DateTime value, eliminating the need for extensive data validation. It also automatically considers the computer's regional settings when displaying various date and time formats.
The control uses a relatively small amount of screen real estate, being about the same size as a single line text box, as seen in Figure 16-2. By default, it displays the current date in the Long Date format currently in effect on the user's machine, although this is configurable with the Format and CustomFormat properties. Clicking on the drop-down arrow at the right end of the control drops down a graphical calendar that shows the selected month with both the selected date and today's date (DateTime.Today) indicated, as shown in Figure 16-3. The colors of the different parts of the calendar can be set, as demonstrated below.
Once the calendar is dropped down, the user can change months by clicking on scroll buttons to either side of the month and year in the title or by clicking on the month name in the title and selecting from the drop-down menu. If the user clicks on the year in the title, up-down scroll buttons appear, which enable scrolling to a new year.
Table 16-11 lists many of the most commonly used properties of the DateTimePicker class.
Property |
Value type |
Description |
---|---|---|
CalendarFont |
Font |
Read/write. The font applied to the calendar. |
CalendarForeColor |
Color |
Read/write. The color of the text used for the calendar dates and the Today legend. |
CalendarMonthBackground |
Color |
Read/write. The background color of the calendar month. |
CalendarTitleBackColor |
Color |
Read/write. The background color of the calendar title. |
CalendarTitleForeColor |
Color |
Read/write. The color of the text used for the calendar title and the selected date. |
CalendarTrailingForeColor |
Color |
Read/write. The color of the text used for the calendar trailing dates, which precede and follow the current month in the calendar. |
Checked |
Boolean |
Read/write. If true (the default), the Value property has been set with a valid value and the displayed value can be updated. |
CustomFormat |
String |
Read/write. A string comprised of format strings (listed in Table 16-9) and/or string literals (escaped with single quotes), representing custom DateTime formats. Default is null (Nothing). Format property must be set to DateTimePickerFormat.Custom for this property to have any effect. |
DropDownAlign |
LeftRightAlignment |
Read/write. Specifies the left/right alignment of the calendar. Default is LeftRightAlignment.Left. Other possible value is LeftRightAlignment.Right. |
Format |
DateTimePickerFormat |
Read/write. Specifies the format for date and time. Must be a member of the DateTimePickerFormat enumeration, listed in Table 16-12. Actual displayed formats are determined by the user's operating system and regional settings. Default is DateTimePickerFormat.Long. |
MaxDate |
DateTime |
Read/write. The maximum date and time that can be selected by the control. The default is 12/31/9998 23:59:59. |
MinDate |
DateTime |
Read/write. The minimum date and time that can be selected by the control. The default is 1/1/1753 00:00:00. |
PreferredHeight |
Integer |
Read-only. Minimum height of the control, in pixels, to accommodate the displayed text. |
ShowCheckBox |
Boolean |
Read/write. If true, a checkbox is displayed to the left of the date-time value in the control. When this checkbox is checked, the values in the control can be changed by the user at runtime by highlighting a portion of the date and pressing the arrow keys. When not checked, the date can only be changed by clicking the drop-down menu to display the calendar. Default is false. |
ShowUpDown |
Boolean |
Read/write. If true, the calendar drop-down menu is disabled and up-down scroll buttons are displayed to the right of the date-time value in the control, which are used to increment/decrement the highlighted portion of the date-time value. Default is false. |
Text |
String |
Read/write. Overridden. Equivalent to the Value property with the appropriate or custom formatting applied. |
Value |
DateTime |
Read/write. The DateTime value assigned to the control. If Value has not yet been set, it returns the current date and time (DateTime.Now). |
Value |
Description |
---|---|
Custom |
Date-time values displayed in a custom format |
Long |
Date-time values displayed in the long format set by the user's OS and regional settings |
Short |
Date-time values displayed in the short format set by the user's OS and regional settings |
Time |
Date-time values displayed in the time format set by the user's OS and regional settings |
The DateTimePicker class has several events that are not inherited from base classes, all listed in Table 16-13. The most commonly used event is ValueChanged, which is raised whenever the Value property changes (i.e., whenever the user selects a new date or time).
Event |
Event argument |
Description |
---|---|---|
CloseUp |
EventArgs |
Raised when the drop-down calendar disappears |
DropDown |
EventArgs |
Raised when the drop-down calendar is shown |
FormatChanged |
EventArgs |
Raised when the Format property is changed |
ValueChanged |
EventArgs |
Raised when the Value property changes |
The programs listed in Example 16-1 (in C#) and Example 16-2 (in VB.NET) demonstrate the DateTimePicker control, along with several different formatting capabilities of the control and of the DateTime class. The programs consist of a form with a DateTimePicker control that displays the date by using a custom format. Below the control is the selected date in several different formats, including the generic ToString, long date and time, short date and time, and a custom format that reflects the custom string that the user enters in a text box.
The program looks like Figure 16-2 after a new date has been selected and a different custom string is entered in the text box.
Figure 16-2. DateTimePicker program
Figure 16-3 shows what the calendar drop-down menu looks like. This monochrome book does not do justice to the vibrant color scheme applied to the calendar, but in real life the title text is blue, the title background is lime green, the calendar itself is yellow with red text, and the trailing dates are a shade of pink. (Hopefully you will not inflict such garish aesthetics on your users.) The code listings show how this color scheme was achieved.
Figure 16-3. DateTimePicker calendar
Today's date is circled on the calendar when the current month is displayed. Clicking on the Today circle at the bottom of every month will instantly select today's date. It is not possible to suppress the Today circle.
An analysis follows the code listings.
Example 16-1. DateTimePicker in C# (DateTimePicker.cs)
using System; using System.Drawing; using System.Windows.Forms; namespace ProgrammingWinApps { public class DTPicker : Form { DateTimePicker dtp; Label lblToString; Label lblLongDate; Label lblLongTime; Label lblShortDate; Label lblShortTime; Label lblCustomFormat; TextBox txtCustomString; public DTPicker( ) { Text = "DateTimePicker Demo"; Size = new Size(400,300); this.Load += new EventHandler(this_Load); dtp = new DateTimePicker( ); dtp.Parent = this; dtp.Location = new Point(20,20); dtp.Size = new Size(ClientSize.Width - 40, dtp.PreferredHeight); dtp.Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right; Font fnt = new Font("Times New Roman", 16); dtp.CalendarFont = new Font(fnt, FontStyle.Bold | FontStyle.Italic); dtp.CalendarForeColor = Color.Red; dtp.CalendarMonthBackground = Color.Yellow; dtp.CalendarTitleBackColor = Color.Lime; dtp.CalendarTitleForeColor = Color.Blue; dtp.CalendarTrailingForeColor = Color.FromArgb(255,192,192); dtp.CustomFormat = "dddd,MMMM d, yyyy 'at' h:mm:ss tt"; dtp.Format = DateTimePickerFormat.Custom; dtp.DropDownAlign = LeftRightAlignment.Right; dtp.ShowUpDown = false; // default dtp.ValueChanged += new EventHandler(dtp_ValueChanged); Label lbl1 = new Label( ); lbl1.Parent = this; lbl1.Text = "ToString:"; lbl1.Location = new Point(dtp.Left, dtp.Bottom + 20); lblToString = new Label( ); lblToString.Parent = this; lblToString.Size = new Size(200, lblToString.PreferredHeight + 10); lblToString.Location = new Point(lbl1.Right, dtp.Bottom + 20); Label lbl2 = new Label( ); lbl2.Parent = this; lbl2.Text = "Long Date:"; lbl2.Location = new Point(dtp.Left, lblToString.Bottom); lblLongDate = new Label( ); lblLongDate.Parent = this; lblLongDate.Size = new Size(200, lblLongDate.PreferredHeight + 10); lblLongDate.Location = new Point(lbl1.Right, lblToString.Bottom ); Label lbl3 = new Label( ); lbl3.Parent = this; lbl3.Text = "Long Time:"; lbl3.Location = new Point(dtp.Left, lblLongDate.Bottom); lblLongTime = new Label( ); lblLongTime.Parent = this; lblLongTime.Size = lblLongDate.Size; lblLongTime.Location = new Point(lbl1.Right, lblLongDate.Bottom); Label lbl4 = new Label( ); lbl4.Parent = this; lbl4.Text = "Short Date:"; lbl4.Location = new Point(dtp.Left, lblLongTime.Bottom); lblShortDate = new Label( ); lblShortDate.Parent = this; lblShortDate.Size = lblLongDate.Size; lblShortDate.Location = new Point(lbl1.Right, lblLongTime.Bottom); Label lbl5 = new Label( ); lbl5.Parent = this; lbl5.Text = "Short Time:"; lbl5.Location = new Point(dtp.Left, lblShortDate.Bottom); lblShortTime = new Label( ); lblShortTime.Parent = this; lblShortTime.Size = lblLongDate.Size; lblShortTime.Location = new Point(lbl1.Right, lblShortDate.Bottom); Label lbl6 = new Label( ); lbl6.Parent = this; lbl6.Text = "Custom String:"; lbl6.Location = new Point(dtp.Left, lblShortTime.Bottom); txtCustomString = new TextBox( ); txtCustomString.Parent = this; txtCustomString.Size = lblLongDate.Size; txtCustomString.Location = new Point(lbl1.Right, lblShortTime.Bottom); txtCustomString.Text = "D"; txtCustomString.TextChanged += new EventHandler(txtCustomString_TextChanged); Label lbl7 = new Label( ); lbl7.Parent = this; lbl7.Text = "Custom Format:"; lbl7.Location = new Point(dtp.Left, txtCustomString.Bottom + 5); lblCustomFormat = new Label( ); lblCustomFormat.Parent = this; lblCustomFormat.Size = new Size(lblLongDate.Width, lblLongDate.Height * 3); lblCustomFormat.Location = new Point(lbl1.Right, txtCustomString.Bottom + 5); } // close for constructor static void Main( ) { Application.Run(new DTPicker( )); } private void UpdateLabels( ) { lblToString.Text = dtp.Value.ToString( ); lblLongDate.Text = dtp.Value.ToLongDateString( ); lblLongTime.Text = dtp.Value.ToLongTimeString( ); lblShortDate.Text = dtp.Value.ToShortDateString( ); lblShortTime.Text = dtp.Value.ToShortTimeString( ); lblCustomFormat.Text = dtp.Value.ToString(txtCustomString.Text); } private void this_Load(object sender, EventArgs e) { UpdateLabels( ); } private void dtp_ValueChanged(object sender, EventArgs e) { UpdateLabels( ); } private void txtCustomString_TextChanged(object sender, EventArgs e) { UpdateLabels( ); } } // close for form class } // close form namespace
Example 16-2. DateTimePicker in VB.NET (DateTimePicker.vb)
Option Strict On imports System imports System.Drawing imports System.Windows.Forms namespace ProgrammingWinApps public class DTPicker : inherits Form dim dtp as DateTimePicker dim lblToString as Label dim lblLongDate as Label dim lblLongTime as Label dim lblShortDate as Label dim lblShortTime as Label dim lblCustomFormat as Label dim txtCustomString as TextBox public sub New( ) Text = "DateTimePicker Demo" Size = new Size(400,300) AddHandler me.Load, AddressOf me_Load dtp = new DateTimePicker( ) dtp.Parent = me dtp.Location = new Point(20,20) dtp.Size = new Size(ClientSize.Width - 40, dtp.PreferredHeight) dtp.Anchor = AnchorStyles.Top or AnchorStyles.Left or _ AnchorStyles.Right dim fnt as new Font("Times New Roman", 16) dtp.CalendarFont = new Font(fnt, FontStyle.Bold or _ FontStyle.Italic) dtp.CalendarForeColor = Color.Red dtp.CalendarMonthBackground = Color.Yellow dtp.CalendarTitleBackColor = Color.Lime dtp.CalendarTitleForeColor = Color.Blue dtp.CalendarTrailingForeColor = Color.FromArgb(255,192,192) dtp.CustomFormat = "dddd,MMMM d, yyyy 'at' h:mm:ss tt" dtp.Format = DateTimePickerFormat.Custom dtp.DropDownAlign = LeftRightAlignment.Right dtp.ShowUpDown = false ' default AddHandler dtp.ValueChanged, AddressOf dtp_ValueChanged dim lbl1 as new Label( ) lbl1.Parent = me lbl1.Text = "ToString:" lbl1.Location = new Point(dtp.Left, dtp.Bottom + 20) lblToString = new Label( ) lblToString.Parent = me lblToString.Size = new Size(200, _ lblToString.PreferredHeight + 10) lblToString.Location = new Point(lbl1.Right, dtp.Bottom + 20) dim lbl2 as new Label( ) lbl2.Parent = me lbl2.Text = "Long Date:" lbl2.Location = new Point(dtp.Left, lblToString.Bottom) lblLongDate = new Label( ) lblLongDate.Parent = me lblLongDate.Size = new Size(200, _ lblLongDate.PreferredHeight + 10) lblLongDate.Location = new Point(lbl1.Right, lblToString.Bottom) dim lbl3 as new Label( ) lbl3.Parent = me lbl3.Text = "Long Time:" lbl3.Location = new Point(dtp.Left, lblLongDate.Bottom) lblLongTime = new Label( ) lblLongTime.Parent = me lblLongTime.Size = lblLongDate.Size lblLongTime.Location = new Point(lbl1.Right, lblLongDate.Bottom) dim lbl4 as new Label( ) lbl4.Parent = me lbl4.Text = "Short Date:" lbl4.Location = new Point(dtp.Left, lblLongTime.Bottom) lblShortDate = new Label( ) lblShortDate.Parent = me lblShortDate.Size = lblLongDate.Size lblShortDate.Location = new Point(lbl1.Right, lblLongTime.Bottom) dim lbl5 as new Label( ) lbl5.Parent = me lbl5.Text = "Short Time:" lbl5.Location = new Point(dtp.Left, lblShortDate.Bottom) lblShortTime = new Label( ) lblShortTime.Parent = me lblShortTime.Size = lblLongDate.Size lblShortTime.Location = new Point(lbl1.Right, _ lblShortDate.Bottom) dim lbl6 as new Label( ) lbl6.Parent = me lbl6.Text = "Custom String:" lbl6.Location = new Point(dtp.Left, lblShortTime.Bottom) txtCustomString = new TextBox( ) txtCustomString.Parent = me txtCustomString.Size = lblLongDate.Size txtCustomString.Location = new Point(lbl1.Right, _ lblShortTime.Bottom) txtCustomString.Text = "D" AddHandler txtCustomString.TextChanged, _ AddressOf txtCustomString_TextChanged dim lbl7 as new Label( ) lbl7.Parent = me lbl7.Text = "Custom Format:" lbl7.Location = new Point(dtp.Left, txtCustomString.Bottom + 5) lblCustomFormat = new Label( ) lblCustomFormat.Parent = me lblCustomFormat.Size = new Size(lblLongDate.Width, _ lblLongDate.Height * 3) lblCustomFormat.Location = new Point(lbl1.Right, _ txtCustomString.Bottom + 5) end sub ' close for constructor public shared sub Main( ) Application.Run(new DTPicker( )) end sub private sub UpdateLabels( ) lblToString.Text = dtp.Value.ToString( ) lblLongDate.Text = dtp.Value.ToLongDateString( ) lblLongTime.Text = dtp.Value.ToLongTimeString( ) lblShortDate.Text = dtp.Value.ToShortDateString( ) lblShortTime.Text = dtp.Value.ToShortTimeString( ) lblCustomFormat.Text = dtp.Value.ToString(txtCustomString.Text) end sub private sub me_Load(ByVal sender as object, _ ByVal e as EventArgs) UpdateLabels( ) end sub private sub dtp_ValueChanged(ByVal sender as object, _ ByVal e as EventArgs) UpdateLabels( ) end sub private sub txtCustomString_TextChanged(ByVal sender as object, _ ByVal e as EventArgs) UpdateLabels( ) end sub end class end namespace
The formatted contents of the labels are updated when the form first loads, every time the value of the DateTimePicker control changes, and whenever the contents of the custom string text box changes. In the constructor, event handlers are created for each event:
this.Load += new EventHandler(this_Load); dtp.ValueChanged += new EventHandler(dtp_ValueChanged); txtCustomString.TextChanged += new EventHandler(txtCustomString_TextChanged);
AddHandler me.Load, AddressOf me_Load AddHandler dtp.ValueChanged, AddressOf dtp_ValueChanged AddHandler txtCustomString.TextChanged, AddressOf txtCustomString_TextChanged
To accomplish the update, a helper method, UpdateLabels, is created and then called in each of the event handlers. UpdateLabels sets the Text properties of the labels with the appropriate formatting method:
private void UpdateLabels( ) { lblToString.Text = dtp.Value.ToString( ); lblLongDate.Text = dtp.Value.ToLongDateString( ); lblLongTime.Text = dtp.Value.ToLongTimeString( ); lblShortDate.Text = dtp.Value.ToShortDateString( ); lblShortTime.Text = dtp.Value.ToShortTimeString( ); lblCustomFormat.Text = dtp.Value.ToString(txtCustomString.Text); }
private sub UpdateLabels( ) lblToString.Text = dtp.Value.ToString( ) lblLongDate.Text = dtp.Value.ToLongDateString( ) lblLongTime.Text = dtp.Value.ToLongTimeString( ) lblShortDate.Text = dtp.Value.ToShortDateString( ) lblShortTime.Text = dtp.Value.ToShortTimeString( ) lblCustomFormat.Text = dtp.Value.ToString(txtCustomString.Text) end sub
Notice that the ToString method is called twice using different overloaded versions: once with no arguments and once with a formatting string provided by the contents of the Custom String text box.
Back in the constructor, the DateTimePicker control has its various properties set. The CalendarFont, CalendarForeColor, CalendarMonthBackground, CalendarTitleBackColor, CalendarTitleForeColor, and CalendarTrailingForeColor properties are all set to control the appearance of the calendar. Notice how the last color property is set using an overloaded form of the static FromArgb method to pass in RGB values rather than a color name.
dtp.CalendarTrailingForeColor = Color.FromArgb(255,192,192);
The CustomFormat and Format properties work in tandem to display the date in the custom format shown. The format string is comprised of the formatting characters in Table 16-9. The word "at" is escaped with single quotes. If that were not the case, then the "t" in "at" would have been interpreted as a formatting character corresponding to a one-letter abbreviation for AM/PM.
Also, the components of the string do not always fill the space allocated to that component in the control. For example, there are extra spaces between Monday and the comma in Figure 16-2, as well as extra spaces between the "at" and the 9. As you change the values, you will realize that the space allocated for each component is equal to the necessary maximum, and each component is then centered within that allocated space.
If the Format property were not specified, it would have defaulted to the long date format, as shown on the form in Figure 16-2. If the Format property were set to DateTimePickerFormat.Time, then the control could be used to set the time, although the drop-down calendar would be of little use in that case: the time would always be set to the current time.
The time can be set however, along with all the other date-time components, if the ShowUpDown property is set to true. Setting that property replaces the calendar drop-down button with up-down scroll buttons. This disables the drop-down calendar. Each component can then be individually highlighted and scrolled either with the scroll buttons or the arrow keys. Figure 16-4 shows the control with the same custom formatting as Figure 16-2, but with both the ShowUpDown and ShowCheckBox properties set true. The latter property locks the displayed value if the checkbox is unchecked, preventing inadvertent changes.
Figure 16-4. DateTimePicker with ShowUpDown and ShowCheckBox properties set
As you will see in Example 16-6 and Example 16-7, a DateTimePicker can also set a TimeSpan value, although you must take a few extra steps to do so.