4

I want to make a panel, which groups buttons by itself:

HWND my_panel = CreateWindow(
    "STATIC",
    "",
    WS_VISIBLE | WS_CHILD | WS_BORDER,
    30,
    100,
    300,
    300,
    main_window, // main dialog
    NULL,
    ( HINSTANCE ) GetWindowLong( main_window, GWL_HINSTANCE ),
    NULL
);

Then I add a button to this panel:

HWND button_in_a_group = CreateWindow(
    "BUTTON",
    "Hello world",
    WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON,
    20,
    20,
    50,
    50,
    my_panel, // as a child for above
    NULL,
    ( HINSTANCE ) GetWindowLong( main_window, GWL_HINSTANCE ),
    NULL
);

When I click the button, it doesn't send a WM_COMMAND but WM_PARENTNOTIFY to callback function. Then, if I press Enter, it works - WM_COMMAND is sent by the button.

How to enable mouse click on nested button, and why nested windows doesn't work as expected?

  • *to callback function* ? button send messages to parent window, not to some function – RbMm Oct 28 '17 at 23:32

1 Answers1

3

Messages are sent to parent window. In this case the static windows is the button's parent. So the main window is not receiving button messages, except WM_PARENTNOTIFY.

You can subclass the static window:

SetWindowSubclass(my_panel, ChildProc, 0, 0);

Define a ChildProc to catch the button messages. See also Subclassing Controls

The button also requires an identifier as follows:

CreateWindow("BUTTON", "Hello world", ... my_panel, HMENU(BUTTON_ID) ...);

WM_COMMAND message is sent to ChildProc when button is clicked. The BN_CLICKED notification carries BUTTON_ID

Note, SetWindowSubclass needs additional header and library:

#include <CommCtrl.h>
#pragma comment(lib, "Comctl32.lib") //Visual Studio option for adding libraries
...
LRESULT CALLBACK ChildProc(HWND hwnd, UINT msg, 
    WPARAM wParam, LPARAM lParam, UINT_PTR, DWORD_PTR)
{
    switch(msg) {
    case WM_COMMAND:
        switch(LOWORD(wParam)) {
        case BUTTON_ID:
            MessageBox(0, "hello world", 0, 0);
            break;
        }
        break;
    case WM_NCDESTROY:
        RemoveWindowSubclass(hwnd, ChildProc, 0);
        break;
    }
    return DefSubclassProc(hwnd, msg, wParam, lParam);
}
Barmak Shemirani
  • 30,904
  • 6
  • 40
  • 77
  • 1
    The STATIC control also needs the `WS_EX_CONTROLPARENT` [extended window style](https://msdn.microsoft.com/en-us/library/windows/desktop/ff700543.aspx) so that keyboard navigation works. Arguably a better solution would be to not introduce a child/parent relationship at all, and make the frame and buttons siblings. It's just a lot easier to manage, and doesn't change the perceived visual representation. – IInspectable Oct 28 '17 at 23:56
  • I tried `SetWindowLongPtr( my_panel, GWLP_WNDPROC, ( LONG_PTR ) DlgMain );` as the `SetWindowSubclass` didn't compile. It works - but now `my_panel` seems to be invisible. Did I miss something? `ShowWindow` takes no effect. – creepyman900 Oct 29 '17 at 00:11
  • You probably missed the header and lib. See update answer. – Barmak Shemirani Oct 29 '17 at 00:17
  • @BarmakShemirani Works perfect, but I had to `#define _WIN32_WINNT 0x0501` before `#include `. It is good to point out this requires version 5.81 or newer of ComCtl32.dll. – creepyman900 Oct 29 '17 at 00:52
  • Yes, `0x0501` is for Windows XP, and ComCtl32.dll 5.81 was available that far back. It's reasonable to make that the minimum target. – Barmak Shemirani Oct 29 '17 at 01:47
  • Just for completeness, "old school" subclassing prior this `SetWindowSubclass` API was done with `GetWindowLong` to save the previous WndProc of the control, then `SetWindowLong` to replace the control's current window proc with a custom one. And in the end of the custom one, `CallWindowProc` to the old one. It was quite ugly hacker stuff... and possibly no longer supported. – Lundin May 02 '19 at 11:54