I know, there must be a way to embed an (modeless) dialog as child of a window created with CreateWindow
. In my case I want to embed them into an scroll-able container window, whereby this container windows it self is a child of the main window (see picture).
The first problem that I encounter is, that I still want be able to use TAB keys and other dialog specific navigation. But how?
My message loop:
while (GetMessage(&msg, NULL, 0, 0)) {
if (IsDialogMessage(msg.hwnd, &msg)) continue;
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
Edit: For testing purpose I modified the loop:
while (GetMessage(&msg, NULL, 0, 0)) {
if (IsEmbeddedDialogWindow(msg.hwnd)) {
if (IsDialogMessage(msg.hwnd, &msg)) continue;
}
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
while (GetMessage(&msg, NULL, 0, 0)) {
if (IsScrollableContainerWindow(msg.hwnd)) {
if (IsDialogMessage(msg.hwnd, &msg)) continue;
}
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
More information how to make it the right way can be found here: Using the TAB key to navigate in non-dialogs, redux
With this message loop, nothing happens if I want to enter some dialog texts as if the message is not handled. If IsDialogMessage
is removed, I can enter some text into an edit control in one of the embedded dialogs, however dialog navigation doesn't work as intended. Of course, WS_TABSTOP
style is set for dialog child controls.
The scroll-able container is created with CreateWindowEx
with styles
WS_CHILD, WS_VISIBLE, WS_VSCROLL, WS_TABSTOP, WS_EX_CONTROLPARENT
and dialogs are created as children of this container.
HWND hWndContainer = GroupBarPanelCreate(WS_CHILD | WS_VISIBLE | WS_VSCROLL | WS_TABSTOP, WS_EX_CONTROLPARENT, hWndMain, 0, 0, 400, 400);
GROUPBAR_PANEL* GroupBarPanel = (GROUPBAR_PANEL*) GetWindowLongPtr(hWndContainer, 0);
// Test embedding dialogs
for (unsigned int i = 0; i < 10; i++) {
HWND hWndDlg = CreateDialogParam(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWndContainer, About, 0);
GroupBarPanelInternalAddLast(GroupBarPanel, hWndChild, hWndDlg, nullptr);
}
My GroupBarPanelInternalAddLast
modify modeless dialog styles by removing caption and borders, and ensures that WS_CHILD
, WS_VISIBLE
and WS_TABSTOP
is set (SetWindowLong(hWndDlg, GWL_STYLE, ...)
). (Also tested WS_EX_CONTROLPARENT
style) With SetParent(hWndDlg, hWndContainer)
modeless dialogs parent is changed.
So what am I missing here? As I found out, neither the container window procedure nor the embedded (for testing purpose subclassed) dialog procedure almost never gets WM_SETFOCUS
or WM_KILLFOCUS
messages for example, but why that?
Solution: Calling IsDialogMessage for the top level window.
while (GetMessage(&msg, NULL, 0, 0)) {
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) {
if (IsDialogMessage(hWndTopLevel, &msg)) {
continue;
}
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
IsDialogMessage
needs to be passed theHWND
of the candidate dialog, not the window the message is targeted at. – Jonathan PotterHWND
of the window that has theWS_EX_CONTROLPARENT
style, and which is the parent of the other dialogs. – Chris BeckeIsDialogMessage()
call is still wrong. You don't want to callIsDialogMessage()
only when the message is sent to the dialog. You want to call it for all messages, passing the HWND of the topmost window that contains the sub-dialogs — in your case, of the Win32AppDemo1 window. – andlabs