I can construct and show a date picker control. I can change the date displayed in the control by clicking on it.
The problem is that I cannot get the notification when the date is changed.
Here is the complete, minimal code for an application showing the problem. The intention to for the console to show a message when the notification is received, either in the message loop or in the window message handler.
#include <windows.h>
#include <iostream>
#include "commctrl.h"
// Add common controls V6 to manifest
#pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
// Message handler
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_DESTROY:
PostQuitMessage(0);
return 0;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
FillRect(hdc, &ps.rcPaint, (HBRUSH)(COLOR_WINDOW 1));
EndPaint(hwnd, &ps);
}
return 0;
case DTN_DATETIMECHANGE:
std::cout << " date change\n";
return 0;
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
HWND ConstructMainWindow()
{
// Register the window class.
const char CLASS_NAME[] = "Sample Window Class";
WNDCLASSA wc = {};
wc.lpfnWndProc = WindowProc;
wc.hInstance = NULL;
wc.lpszClassName = CLASS_NAME;
RegisterClassA(&wc);
// Create the window.
HWND hwnd = CreateWindowExA(
0, // Optional window styles.
CLASS_NAME, // Window class
"Date Picker Test", // Window text
WS_OVERLAPPEDWINDOW, // Window style
// Size and position
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
NULL, // Parent window
NULL, NULL, NULL);
if (hwnd == NULL)
{
std::cout << "main window failed\n";
exit(1);
}
return hwnd;
}
void ConstructDatePicker(HWND main)
{
// initialize date picker control
INITCOMMONCONTROLSEX icex;
icex.dwSize = sizeof(icex);
icex.dwICC = ICC_DATE_CLASSES;
if (!InitCommonControlsEx(&icex))
std::cout << "InitCommonControlsEx failed\n";
// send messages to WindowProc
WNDCLASSA wc = {};
wc.lpfnWndProc = &WindowProc;
wc.hInstance = NULL;
wc.lpszClassName = "DateTime";
RegisterClassA(&wc);
CreateWindowExA(0,
DATETIMEPICK_CLASSA,
"DateTime",
WS_BORDER | WS_CHILD | WS_VISIBLE | DTS_SHOWNONE,
30, 30, 200, 30,
main,
NULL,
NULL,
NULL);
}
int main()
{
HWND hwnd = ConstructMainWindow();
ConstructDatePicker(hwnd);
ShowWindow(hwnd, 1);
// Run the message loop.
MSG msg = {};
while (GetMessage(&msg, NULL, 0, 0) > 0)
{
if( msg.message == DTN_DATETIMECHANGE)
std::cout << "message " << msg.message << "\n";
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
After fix from Remy Lebeau
case WM_NOTIFY:
{
NMHDR *pnmhdr = reinterpret_cast<NMHDR *>(lParam);
if (pnmhdr->code == DTN_DATETIMECHANGE)
{
LPNMDATETIMECHANGE lpChange = (LPNMDATETIMECHANGE)(lParam);
std::cout << " date change to " <<
lpChange->st.wYear <<"/" <<
lpChange->st.wMonth <<"/" <<
lpChange->st.wDay <<" "
<< lpChange->dwFlags << "\n";
}
}
Output is
date change to 2022/12/1 0
date change to 2022/12/1 0
CodePudding user response:
DO NOT call RegisterClass()
for system-provided classes. They are already registered by the OS. You are not supposed to register a custom WindowProc
for system classes. If you want to hook a system-provided UI control, use SetWindowLongPtr(GWLP_WNDPROC)
or SetWindowSubclass()
instead. See Subclassing Controls on MSDN.
However, that being said, there is no need to do that in this case, because the DTN_DATETIMECHANGE
notification is sent to the DTP's parent window (ie, your main window), which you already have a WindowProc
registered for. The reason you are not seeing the notification is because you are catching it incorrectly. DTN_DATETIMECHANGE
(-759) is not the actual message code, so your switch
never jumps to that case
. The notification is actually wrapped inside of a WM_NOTIFY
message, which you would have known if you had read the documentation:
https://learn.microsoft.com/en-us/windows/win32/controls/dtn-datetimechange
Sent by a date and time picker (DTP) control whenever a change occurs. This notification code is sent in the form of a WM_NOTIFY message.
https://learn.microsoft.com/en-us/windows/win32/controls/wm-notify
Sent by a common control to its parent window when an event has occurred or the control requires some information.
So, try this instead:
#include <windows.h>
#include <iostream>
#include "commctrl.h"
// Add common controls V6 to manifest
#pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
// Message handler
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_CREATE:
CreateWindowExA(0,
DATETIMEPICK_CLASSA,
"DateTime",
WS_BORDER | WS_CHILD | WS_VISIBLE | DTS_SHOWNONE,
30, 30, 200, 30,
hwnd,
NULL,
NULL,
NULL);
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
FillRect(hdc, &ps.rcPaint, reinterpret_cast<HBRUSH>(COLOR_WINDOW 1));
EndPaint(hwnd, &ps);
}
return 0;
case WM_NOTIFY:
{
NMHDR *pnmhdr = reinterpret_cast<NMHDR*>(lParam);
if (pnmhdr->code == DTN_DATETIMECHANGE)
{
LPNMDATETIMECHANGE lpChange = reinterpret_castt<LPNMDATETIMECHANGE>(lParam);
// use lpChange->dwFlags and lpChange->st as needed...
std::cout << " date change\n";
}
}
return 0;
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
HWND ConstructMainWindow()
{
// Register the window class.
const char CLASS_NAME[] = "Sample Window Class";
WNDCLASSA wc = {};
wc.lpfnWndProc = WindowProc;
wc.lpszClassName = CLASS_NAME;
if (RegisterClassA(&wc) == NULL)
{
std::cout << "main window register class failed\n";
exit(1);
}
// Create the window.
HWND hwnd = CreateWindowExA(
0, // Optional window styles.
CLASS_NAME, // Window class
"Date Picker Test", // Window text
WS_OVERLAPPEDWINDOW, // Window style
// Size and position
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
NULL, // Parent window
NULL, NULL, NULL);
if (hwnd == NULL)
{
std::cout << "main window failed\n";
exit(1);
}
return hwnd;
}
int main()
{
// initialize date picker control
INITCOMMONCONTROLSEX icex;
icex.dwSize = sizeof(icex);
icex.dwICC = ICC_DATE_CLASSES;
if (!InitCommonControlsEx(&icex))
{
std::cout << "InitCommonControlsEx failed\n";
return 1;
}
HWND hwnd = ConstructMainWindow();
ShowWindow(hwnd, 1);
// Run the message loop.
MSG msg = {};
while (GetMessage(&msg, NULL, 0, 0) > 0)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}