Using Date and Time Picker Controls
This section provides information and sample code for implementing date and time picker controls.
The following topics are discussed:
- Creating a Date and Time Picker Control
- Processing Date and Time Picker Notifications
- Processing the DTN_DATETIMECHANGE Notification
- Supporting Callback Fields in a DTP control
- Related Topics
To create a date and time picker (DTP) control, use the CreateWindowEx function, specifying DATETIMEPICK_CLASS as the window class. You must first register the window class by calling the InitCommonControlsEx function, while specifying the ICC_DATE_CLASSES bit in the accompanying INITCOMMONCONTROLSEX structure.
The following example creates a DTP control in an existing modeless dialog box. It uses the DTS_SHOWNONE style to enable the user to simulate deactivation of the date within the control.
// CreateDatePick creates a DTP control within a dialog box.
// Returns the handle to the new DTP control if successful, or NULL
// otherwise.
//
// hwndMain - The handle to the main window.
// g_hinst - global handle to the program instance.
HWND WINAPI CreateDatePick(hwndMain)
{
HWND hwndDP = NULL;
HWND hwndDlg = NULL;
INITCOMMONCONTROLSEX icex;
icex.dwSize = sizeof(icex);
icex.dwICC = ICC_DATE_CLASSES;
InitCommonControlsEx(&icex);
hwndDlg = CreateDialog (g_hinst,
MAKEINTRESOURCE(IDD_DIALOG1),
hwndMain,
DlgProc);
if(hwnDlg)
hwndDP = CreateWindowEx(0,
DATETIMEPICK_CLASS,
TEXT("DateTime"),
WS_BORDER|WS_CHILD|WS_VISIBLE|DTS_SHOWNONE,
20,50,220,20,
hwndDlg,
NULL,
g_hinst,
NULL);
return (hwndDP);
}
You can also drag a Date Time Picker control from the Toolbox in the Microsoft Visual Studio resource editor.
The following example processes the DTN_DATETIMECHANGE, DTN_FORMAT, DTN_FORMATQUERY, and DTN_WMKEYDOWN notifications by calling the DoDateTimeChange, DoFormatQuery, DoFormat, and DoWMKeydown application-defined functions.
Other topics in this chapter provide additional information on these notifications. See Supporting Callback Fields and Processing the DTN_DATETIMECHANGE Notification Message for additional information.
BOOL WINAPI DoNotify(HWND hwnd, WPARAM wParam, LPARAM lParam)
{
LPNMHDR hdr = (LPNMHDR)lParam;
switch(hdr->code){
case DTN_DATETIMECHANGE:{
LPNMDATETIMECHANGE lpChange = (LPNMDATETIMECHANGE)lParam;
DoDateTimeChange(lpChange);
}
break;
case DTN_FORMATQUERY:{
LPNMDATETIMEFORMATQUERY lpDTFQuery = (LPNMDATETIMEFORMATQUERY)lParam;
// Process DTN_FORMATQUERY to ensure that the control
// displays callback information properly.
DoFormatQuery(hdr->hwndFrom, lpDTFQuery);
}
break;
case DTN_FORMAT:{
LPNMDATETIMEFORMAT lpNMFormat = (LPNMDATETIMEFORMAT) lParam;
// Process DTN_FORMAT to supply information about callback
// fields (fields) in the DTP control.
DoFormat(hdr->hwndFrom, lpNMFormat);
}
break;
case DTN_WMKEYDOWN:{
LPNMDATETIMEWMKEYDOWN lpDTKeystroke =
(LPNMDATETIMEWMKEYDOWN)lParam;
// Process DTN_WMKEYDOWN to respond to a user's keystroke in
// a callback field.
DoWMKeydown(hdr->hwndFrom, lpDTKeystroke);
}
break;
}
// All of the above notifications require the owner to return zero.
return FALSE;
}
A date and time picker (DTP) control sends the DTN_DATETIMECHANGE message whenever a change occurs. This message will be generated if the user changes one of the fields in the control or changes the state of the control's check box (DTS_SHOWNONE only).
The following example is an application-defined function designed to update a static control within a dialog box. The text within the static control is changed to reflect the current state of the DTP control.
void WINAPI DoDateTimeChange(LPNMDATETIMECHANGE lpChange)
{
// If the user has unchecked the DTP's check box, change the
// text in a static control to show the appropriate message.
//
// g_hwndDlg - a program-global address of a dialog box.
if(lpChange->dwFlags == GDT_NONE)
SetDlgItemText(g_hwndDlg, IDC_STATUS, "Disabled");
else
SetDlgItemText(g_hwndDlg, IDC_STATUS, "Active");
}
If you plan to use callback fields with the date and time picker (DTP) controls in your application, you must be prepared to handle DTN_FORMATQUERY, DTN_FORMAT, and DTN_WMKEYDOWN notification messages. For additional information about callback fields, see Callback fields.
This section contains information about how your application can process these notification messages. There are several examples of source code for application-defined functions. The following list shows notification messages with sample functions that process them.
Notification Message | Sample Function |
DTN_FORMATQUERY | DoFormatQuery |
DTN_FORMAT | DoFormat |
DTN_WMKEYDOWN | DoWMKeydown |
A date and time picker (DTP) control sends a DTN_FORMATQUERY notification message to request information about the maximum possible size of a callback field within the control. Handling this message ensures that all fields are displayed properly. The following DoFormatQuery application-defined function processes the DTN_FORMATQUERY notification message by calculating the width of the widest possible string for a given callback field.
Security Alert Using lstrcmp incorrectly can compromise the security of your application. For example, before calling lstrcmp in the following code example you should make sure the two strings are null-terminated. You should review the Security Considerations: Microsoft Windows Controls before continuing.
// DoFormatQuery processes DTN_FORMATQUERY messages to ensure that the
// DTP control displays callback fields properly.
//
void WINAPI DoFormatQuery(
HWND hwndDP,
LPNMDATETIMEFORMATQUERY lpDTFQuery)
{
HDC hdc;
HFONT hFont, hOrigFont;
// Prepare the device context for GetTextExtentPoint32 call.
hdc = GetDC(hwndDP);
hFont = FORWARD_WM_GETFONT(hwndDP, SendMessage);
if(!hFont)
hFont = (HFONT)GetStockObject(DEFAULT_GUI_FONT);
hOrigFont = SelectObject(hdc, hFont);
// Check to see if this is the callback segment desired. If so,
// use the longest text segment to determine the maximum
// width of the callback field, and then place the information into
// the NMDATETIMEFORMATQUERY structure.
if(!lstrcmp("XX",lpDTFQuery->pszFormat))
GetTextExtentPoint32 (hdc,
"366", // widest date string
3,
&lpDTFQuery->szMax);
// Reset the font in the device context; then release the context.
SelectObject(hdc,hOrigFont);
ReleaseDC(hwndDP, hdc);
}
A date and time picker (DTP) control sends the DTN_FORMAT notification to request text that will be displayed in a callback field of the control. By handling this notification message, you allow the DTP control to display information that it does not natively support.
The following DoFormat application-defined function processes DTN_FORMAT notification messages by providing a text string for a callback field. DoFormat calls the GetDayNum application-defined function to request the day number to be used in the callback string.
// DoFormat processes DTN_FORMAT to provide the text for a callback
// field in a DTP control. In this example, the callback field
// contains a value for the day of year. The function calls the
// application-defined function GetDayNum (below) to retrieve
// the correct value. StringCchPrintf truncates the formatted string if
// the entire string will not fit into the destination buffer.
void WINAPI DoFormat(HWND hwndDP, LPNMDATETIMEFORMAT lpDTFormat)
{
HRESULT hr = StringCchPrintf(lpDTFormat->szDisplay,
sizeof(lpDTFormat->szDisplay)/sizeof(lpDTFormat->szDisplay[0]),
"%d",GetDayNum(&lpDTFormat->st));
if(SUCCEEDED(hr))
{
// Insert code here to handle the case when the function succeeds.
}
else
{
// Insert code here to handle the case when the function fails or the string
// is truncated.
}
The application-defined sample function DoFormat calls the following GetDayNum application-defined function to request the day number based on the current date. GetDayNum returns an INT value that represents the current day of the year, from 0 to 366. This function calls another application-defined function, IsLeapYr, during processing.
// GetDayNum is an application-defined function that retrieves the
// correct day of year value based on the contents of a given
// SYSTEMTIME structure. This function calls the IsLeapYr function to
// check if the current year is a leap year. The function returns an
// integer value that represents the day of year.
int WINAPI GetDayNum(SYSTEMTIME *st)
{
int iNormYearAccum[ ] = {31,59,90,120,151,181,
212,243,273,304,334,365};
int iLeapYearAccum[ ] = {31,60,91,121,152,182,
213,244,274,305,335,366};
int iDayNum;
if(IsLeapYr(st->wYear))
iDayNum=iLeapYearAccum[st->wMonth-2]+st->wDay;
else
iDayNum=iNormYearAccum[st->wMonth-2]+st->wDay;
return (iDayNum);
}
The application-defined sample function GetDayNum calls the IsLeapYr function to determine whether the current year is a leap year. IsLeapYr returns a BOOL value that is TRUE if it is a leap year and FALSE otherwise.
// IsLeapYr determines if a given year value is a leap year. The
// function returns TRUE if the current year is a leap year, and
// FALSE otherwise.
BOOL WINAPI IsLeapYr(int iYear)
{
int iQuotient;
BOOL fLeapYr = FALSE;
// If the year is evenly divisible by 4 and not by 100, then this
// is a leap year.
if(!(iYear%4) && (iYear%100))
fLeapYr = TRUE;
else{
// If the year is evenly divisible by 4 and 100, then check to
// see if the quotient of year divided by 100 is also evenly
// divisible by 4. If it is, then this is a leap year.
if(!(iYear%4) && !(iYear%100)){
iQuotient = iYear/100;
if(!(iQuotient%4)) fLeapYr = TRUE;
}
}
return (fLeapYr);
}
Date and time picker (DTP) controls send the DTN_WMKEYDOWN message to report that the user has typed input in a callback field. Handling this message allows you to emulate the same keyboard responses supported for standard DTP fields or provide custom responses. The following DoWMKeydown application-defined function provides an example of how DTN_WMKEYDOWN notifications can be handled.
Security Alert Using lstrcmp incorrectly can compromise the security of your application. For example, before calling lstrcmp in the following code example, you should make sure the two strings are null-terminated. You should review the Security Considerations: Microsoft Windows Controls before continuing.
// DoWMKeydown increments or decrements the day of month according
// to user keyboard input.
void WINAPI DoWMKeydown(
HWND hwndDP,
LPNMDATETIMEWMKEYDOWN lpDTKeystroke)
{
int delta =1;
if(!lstrcmp(lpDTKeystroke->pszFormat,"XX")){
switch(lpDTKeystroke->nVirtKey){
case VK_DOWN:
case VK_SUBTRACT:
delta = -1; // fall through
case VK_UP:
case VK_ADD:
lpDTKeystroke->st.wDay += delta;
break;
}
}
}