Archived

This topic is now archived and is closed to further replies.

Win32: Having a dialog as a child of another window

This topic is 5144 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I''m trying to get a dialog window (created with CreateDialog()), to be the child window of another window (which is created with DialogBox() [although it shouldn''t matter]). The reason is, i''m trying to create a settings page for my app. I have at least 13 pages, and i''ll be adding more, so my property sheet is getting rather crowded. So i was thinking of having a listbox control on the left of my settings dialog, and a space where i could have a dialog for the settings for each page. I''ve tried using SetParent(), but that freezes the app (neither dialog is displayed, but the dialog gets created, because i can;t click on the main app window). Has anyone made a settings page like this before? Cheers, Steve

Share this post


Link to post
Share on other sites
Are you using a modal dialog or a modeless dialog? Your inability to click on the app window while it''s "up" makes me think modal, so I would suggest you look at how modeless dialogs work, because that would prevent the dialog from freezing anything else, which I think is what you''re trying for.

Really though I''m not entirely clear on why you think you need yet another dialog.

-fel

Share this post


Link to post
Share on other sites
Sorry, my explanation is rubbish
Lemme get some code...

Ok. I have a modal dialog for my settings dialog. Heres what my WM_INTIDIALOG handler looks like:

case WM_INITDIALOG:
GetWindowRect(GetDlgItem(hDlg,IDC_POSITION),&rc);
ShowWindow(GetDlgItem(hDlg,IDC_POSITION),SW_HIDE);
hWndPage = CreateDialog(g_hInstance,MAKEINTRESOURCE(IDD_SETTINGS_CONNECTION),hDlg,SettingsDlgProcConnection);
SetParent(hWndPage,hDlg);
ShowWindow(hWndPage,SW_SHOW);
break;

IDC_POSITION is a static control that covers the area i want my ''page'' to be. IDD_SETTINGS_CONNECTION is a dialog resource that i used for my property sheet.
Running that code causes the cursor to change to the hourglass, the main window to loose focus, and for the app to freeze (the main window isn''t redrawn).
If i comment out the SetParent() line, i get this:

As you can see, the settings dialog is set up fine, but its not a child window (perhaps i''m using the wrong word - i mean its not a control on the dialog).

There must be some way to achieve what i''m trying to do, after all - the property sheets do it.
I really don''t want to have to use a standard window, because that''d mean rewriting my settings pages (admitadly not too much, but i''d need a class for each page), but if its the only option then i''ll go for it.

Cheers,
Steve

Share this post


Link to post
Share on other sites
Setting a window as a parent dialog means, well, probably a lot more than you want it to.

What exactly do you want that dialog to do that it won't do merely in terms of what dialog normal behavior is? Modal dialogs won't affect the rest of the application until they return anyway, as a rule.

If you really must have a dialog pointer I'd suggest passing it in on a constructer overload to avoid the "other stuff".

EDIT: looking over your post for the third or fourth time, I think I might see what you're trying to do. It begs the question, though, why aren't you just using PropertySheet from prsht.h? Giving a dialog a parent does not assume that you mean the dialog should be *on* the parent dialog. It just assumes spawn point, return point, and a few other things when you're running modeless, like callback handling.

-fel

[edited by - felisandria on November 10, 2003 2:09:49 PM]

Share this post


Link to post
Share on other sites
I think you''re going to get into a mess doing want you want to do but here I go...

Assuming that you have "Master Dialog" with a list IDC_LIST and a static control IDC_STATIC_PANELAREA and three "panel" dialogs IDD_DIALOG_PANEL1, IDD_DIALOG_PANEL2, and IDD_DIALOG_PANEL3.

In the WM_INITDIALOG message you can create the three child panels and in the LBN_SELCHANGE message for the list you could move and show them. *WARNING! UGLY CODE AHEAD*

LRESULT CALLBACK PropertiesDlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
static HWND wWndPanel1 = 0;
static HWND wWndPanel2 = 0;
static HWND wWndPanel3 = 0;

switch (message)
{
case WM_INITDIALOG:
wWndPanel1 = CreateDialog(hInst,MAKEINTRESOURCE(IDD_DIALOG_PANEL1),hDlg,(DLGPROC)PanelProc);
wWndPanel2 = CreateDialog(hInst,MAKEINTRESOURCE(IDD_DIALOG_PANEL2),hDlg,(DLGPROC)PanelProc);
wWndPanel3 = CreateDialog(hInst,MAKEINTRESOURCE(IDD_DIALOG_PANEL3),hDlg,(DLGPROC)PanelProc);

SendDlgItemMessage( hDlg, IDC_LIST, LB_ADDSTRING, 0, (LPARAM)"Panel 1" );
SendDlgItemMessage( hDlg, IDC_LIST, LB_ADDSTRING, 0, (LPARAM)"Panel 2" );
SendDlgItemMessage( hDlg, IDC_LIST, LB_ADDSTRING, 0, (LPARAM)"Panel 3" );

SendDlgItemMessage( hDlg, IDC_LIST, LB_SELECTSTRING, 0, (LPARAM)"Panel 1" );
return TRUE;

case WM_COMMAND:
if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
{
EndDialog(hDlg, LOWORD(wParam));
return TRUE;
}
else
{
switch ( HIWORD(wParam) )
{
case LBN_SELCHANGE:
{
int nSelected = SendDlgItemMessage( hDlg, IDC_LIST, LB_GETSEL, 0, 0);
HWND hPanel = 0;
hPanel = SendDlgItemMessage( hDlg, IDC_LIST, LB_GETSEL, 0, 0) ? wWndPanel1 : hPanel;
hPanel = SendDlgItemMessage( hDlg, IDC_LIST, LB_GETSEL, 1, 0) ? wWndPanel2 : hPanel;
hPanel = SendDlgItemMessage( hDlg, IDC_LIST, LB_GETSEL, 2, 0) ? wWndPanel3 : hPanel;
if ( hPanel )
{
RECT rect;
GetWindowRect( GetDlgItem( hDlg, IDC_STATIC_PANELAREA ), &rect );
MoveWindow( hPanel, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, TRUE );
ShowWindow( hPanel, SW_NORMAL );
}
}
break;
}
}
break;
}
return FALSE;
}

Sorry for the ugly code. I tried to keep it simple, not pretty.

There are a lot of problem with this approach. For example, the panels will not move when the parent does so you''ll have to watch for it and move them and tabbing between the controls will probably not work as you expect (e.g. you will not be able to tab back out to the list.

Full source here: ftp://ftp.cyberbrinedreams.com/pub/temp_code/multidialog.zip

Share this post


Link to post
Share on other sites
fel: Because i''ve currently got 12 pages, and theres goign to be at least 15. A property sheet just gets messy with too many tabs.

MauMan: Yeah, thats kinda what i wanna do, but i want to have the various pages as controls on the dialog rather than as seperate windows.

AP: I tried that but it didn''t work. I also did a SetWindowPos() afterwards, as the docs say (to update the style).
I can''t remember why, i''ll get back to you.

Cheers,
Steve

Share this post


Link to post
Share on other sites
I've done something like what I think you want. But in this case my dialog was a replacement for the client area of a main window. Should work similarly for your needs.

bool InitWindow()
{
DWORD style;
HWND hWnd=CreateWindow(g_classname,"Picture Album",style=(WS_OVERLAPPED | WS_MINIMIZEBOX | WS_SYSMENU),
CW_USEDEFAULT,0,
CW_USEDEFAULT,0,
NULL,
NULL,
g_hInst,
NULL);
if(hWnd==NULL)
return false;

// create the child

HWND hDlg=CreateDialog(g_hInst,MAKEINTRESOURCE(IDD_CATALOG_VIEW), hWnd, DialogProc);
if(hDlg==NULL)
{
DestroyWindow(hWnd);
return false;
}

// need to resize main window to fit dialog

RECT r_main;
GetWindowRect(hDlg, &r_main);
AdjustWindowRect(&r_main, style, TRUE);
SetWindowPos(hWnd,NULL,0,0,r_main.right-r_main.left,r_main.bottom-r_main.top, SWP_NOZORDER | SWP_NOMOVE);
ShowWindow(hWnd, SW_SHOWNORMAL);
UpdateWindow(hWnd);

return true;
}


And the dialog was made with the WS_CHILD style too btw.



[edited by - Mastaba on November 10, 2003 4:44:01 PM]

Share this post


Link to post
Share on other sites
Ahh... Manually editing the reource file to add WS_CHILD makes the dialog display ok (leaving the call to SetParent() in there), but it doesn't make the page appear on the dialog. I also tried SetWindowPos() to make the page HWND_TOPMOST or above the dialog, but neither made any difference.

Setting the style to include WS_CHILD with SetWindowLong() doesn't seem to have any effect - the app still freezes.

Edit:
Mastaba - I can't see how your code would work, surely it'd just make another dialog which is over the parent window, not like a control in any way?

*sigh*
Looks like i'm gonna have to use 'normal' windows for this...

[edited by - Evil Bill on November 10, 2003 6:36:26 PM]

Share this post


Link to post
Share on other sites
MauMan: That code half works - the page is still a seperate window, and you can move the parent about. I could easily fix that, but i expect there'd be artifacts like the ability to shove a window between the pannel and the parent. Unless i use SetWindowPos() for responsing to every message...

Edit: Oh yeah, i see you mentioned that

I'll give it a try anyway. Cheers

[edited by - Evil Bill on November 10, 2003 6:55:40 PM]

Share this post


Link to post
Share on other sites
Hrm... That causes all sorts of problems...
I remembered why i didn''t want to use ''normal'' windows - I can''t use dialog templates for the pages. I''d need to manually create every control on every page (eek).

Cheers,
Steve

Share this post


Link to post
Share on other sites
quote:

Mastaba - I can''t see how your code would work, surely it''d just make another dialog which is over the parent window, not like a control in any way?




Wrong, it is embedded in the window as a complex control since it is a child of the main window.

Share this post


Link to post
Share on other sites
Holy crapsticks! That code looks... interesting...
For those of you who can''t be bothered getting the source:

void LoadAndCreateControls (HWND hDlg, int iDialog)
{
HGLOBAL hDlgResMem ;
HRSRC hDlgRes ;
BYTE FAR *lpDlgRes ;
RECT rc ;
HFONT hDlgFont ;
LPPOINT pt ;
DWORD style ;
WORD wID ;
BYTE bNumOfCtrls, bCurCtrl ;
LPSTR classname ;
HWND *hwnd, hAnchor ;
int xOffset, yOffset ;

// We need to get the font of the dialog so we can set the font of

// the child controls. If the dialog has no font set, it uses the

// default system font, and hDlgFont equals zero.


hDlgFont = (HFONT) SendMessage (hDlg, WM_GETFONT, 0, 0L) ;

// Load the resource into memory and get a pointer to it.


hDlgRes = FindResource (ghInst,
MAKEINTRESOURCE(diDlgs[iDialog].iResID),
RT_DIALOG) ;
hDlgResMem = LoadResource (ghInst, hDlgRes) ;
lpDlgRes = (BYTE FAR *) LockResource (hDlgResMem) ;

// Advance past the dialog header (DialogBoxHeader structure in online

// help.


style = * (LPDWORD) lpDlgRes ; // Grab the style

lpDlgRes += sizeof (DWORD) ; // Pass the style


bNumOfCtrls = * (LPBYTE) lpDlgRes ; // Grab the number of controls

lpDlgRes += sizeof (BYTE) ; // Pass the number of controls


lpDlgRes += 4 * sizeof (WORD) ; // Pass x, y, cx, and cy of dialog


if (0xFF == *lpDlgRes)
lpDlgRes += 3 ; // Menu by ordinal. Skip 0xff and the ordinal.

else
{ // Menu by name, or no menu at all.

while (*lpDlgRes)
++lpDlgRes ;
++lpDlgRes ;
}

while (*lpDlgRes) ++lpDlgRes ; ++lpDlgRes ; // Pass the class name

while (*lpDlgRes) ++lpDlgRes ; ++lpDlgRes ; // Pass the caption


// Some fields are present only if DS_SETFONT is specified.


if (style & DS_SETFONT)
{
lpDlgRes += sizeof (WORD) ; // Pass point size

while (*lpDlgRes) ++lpDlgRes ; ++lpDlgRes ; // Pass face name

}


// Alloc enough local memory to hold the hwnd and text atom of each ctrl.


diDlgs[iDialog].pCtrlHndls = (HWND NEAR *)
LocalAlloc (LPTR, sizeof (HWND) + sizeof(HWND) * bNumOfCtrls) ;

{
BYTE bazoo ;

bazoo = sizeof (ATOM) * bNumOfCtrls ;

diDlgs[iDialog].pCtrlTexts = (ATOM NEAR *)
LocalAlloc (LPTR, bazoo) ;
}

{
ATOM *pTmp ;
BYTE b = 1 ;

pTmp = diDlgs[iDialog].pCtrlTexts ;

for (b = 0; b < bNumOfCtrls; b++)
pTmp++ ;
}


hwnd = (HWND *) diDlgs[iDialog].pCtrlHndls ;

// The positioning of the controls will be offset according to the

// top-right corner of the IDC_ANCHOR static control.


hAnchor = GetDlgItem (hDlg, IDC_ANCHOR) ;
GetWindowRect (hAnchor, &rc) ;
pt = (LPPOINT) &rc ;
ScreenToClient (hDlg, pt) ;
pt++ ;
ScreenToClient (hDlg, pt) ;
xOffset = rc.right ;
yOffset = rc.top ;

// The rest of the dialog template contains ControlData structures.

// We parse these structures and call CreateWindow() for each.


for (bCurCtrl = 0; bCurCtrl < bNumOfCtrls; bCurCtrl++)
{
// ControlData coordinates are in dialog units. We need to convert

// these to pixels before adding the IDC_ANCHOR offset.


CopyRect (&rc, (LPRECT)lpDlgRes) ; // Get the control''s coordinates.


MapDialogRect (hDlg, &rc) ; // Convert to pixels.

rc.left += xOffset ; // Add the offset.

rc.top += yOffset ;

lpDlgRes += 4 * sizeof (WORD) ; // Pass the control''s coordinates.

wID = * (LPWORD) lpDlgRes ; // Get the control''s ID.

lpDlgRes += sizeof (WORD) ; // Pass the control''s ID.

style = * (LPDWORD) lpDlgRes ; // Get the control''s styles.

lpDlgRes += sizeof (DWORD) ; // Pass the control''s style.


// At this point in the ControlData structure (see "Dialog Box

// Resource" in online help), the class of the control may be

// described either with text, or as a byte with a pre-defined

// meaning.


switch (*lpDlgRes)
{
case 0x80: classname = "button" ; break ;
case 0x81: classname = "edit" ; break ;
case 0x82: classname = "static" ; break ;
case 0x83: classname = "listbox" ; break ;
case 0x84: classname = "scrollbar" ; break ;
case 0x85: classname = "combobox" ; break ;
default:
classname = lpDlgRes ;
while (lpDlgRes) lpDlgRes++ ;
}
lpDlgRes++ ; // Either passes the byte class identifier, or passes

// The null terminator of the class name string.


*hwnd = CreateWindow (classname, lpDlgRes, style,
rc.left, rc.top,
rc.right, rc.bottom,
hDlg, wID, ghInst, NULL) ;

// Even though the font is the right size (MapDialogRect() did this),

// we also need to set the font if it''s not the system font.


if (hDlgFont)
SendMessage (*hwnd, WM_SETFONT, hDlgFont, 0L) ;

while (*lpDlgRes) // Pass the window text.

lpDlgRes++ ;

lpDlgRes++ ; // Null terminator for the window text.

lpDlgRes++ ; // Null terminator for ControlData structure (not well

// documented, but here nonetheless.)


hwnd++ ;
}
*hwnd = 0 ;

diDlgs[iDialog].fPresent = TRUE ;

// Free the resource which we just parsed.


UnlockResource (hDlgResMem) ;
FreeResource (hDlgResMem) ;

// Send the new child a WM_INITDIALOG so it can do control initialization


(*(diDlgs[iCurDlg].fpDlgProc))(hDlg, WM_INITDIALOG, 0, 0L) ;
}


Mastaba: I tried your code: all it does is make a dialo that has a parent of the main window. All that means is that you can''t activate the parent window while the child dialog is active.

Share this post


Link to post
Share on other sites
You weren't supposed to use my code verbatim. I clearly said my code embedded the dialog into the client space of a main window. You can do something similiar by embedding a dialog into a dialog. It's quite trivial. That is after all, how property pages work. And no, my example doesn't make a seperate dialog that makes the main window unactive. It works IN the main window. And the main window's menu is still fully accessable.

[edited by - Mastaba on November 11, 2003 8:42:49 PM]

Share this post


Link to post
Share on other sites
Again, it doesn''t simply make a dialog and move it so that it hides the client space. It is IN the client space. It is a CHILD! Children move with their parent automatically! Give me a few seconds and I clearly show the rest of the freaking WindowProc so you can see there is absolutely no special or tricky code to move the dialog.

Share this post


Link to post
Share on other sites