Home > Net >  How to handle notification from date picker control
How to handle notification from date picker control

Time:12-16

I can construct and show a date picker control. I can change the date displayed in the control by clicking on it.

image

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;
}
  • Related