Date: | March 30, 2005 / year-entry #81 |
Tags: | code |
Orig Link: | https://blogs.msdn.microsoft.com/oldnewthing/20050330-00/?p=36023 |
Comments: | 16 |
Summary: | The dialog template describes what the dialog box should look like, so the dialog manager walks the template and follows the instructions therein. It's pretty straightforward; there isn't much room for decision-making. You just do what the template says. For simplicity, I'm going to assume that the dialog template is an extended dialog template. This... |
The dialog template describes what the dialog box should look like, so the dialog manager walks the template and follows the instructions therein. It's pretty straightforward; there isn't much room for decision-making. You just do what the template says. For simplicity, I'm going to assume that the dialog template is an extended dialog template. This is a superset of the classic DLGTEMPLATE, so there is no loss of generality. Furthermore, I will skip over some of the esoterica (like the WM_ENTERIDLE message) because that would just be distracting from the main point. I am also going to ignore error-checking for the same reason. Finally, I'm going to assume you already understand the structure of the various dialog templates and ignore the parsing issues. (If you've forgotten, you can go back and re-read my series from last June. Most important are parts 2 and 4, and the summary table is a handy quick-reference.) Okay, here we go.
The first order of business is to study the dialog styles and
translate the
Question: Why does the
Answer: To make it easier for people to convert an
existing dialog into a If the template includes a menu, the menu is loaded from the instance handle passed as part of the creation parameters. hmenu = LoadMenu(hinst, <resource identifier in template>); This is a common theme in dialog creation: The instance handle you pass to the dialog creation function is used for all resource-related activities during dialog creation. The algorithm for getting the dialog font goes like this: if (DS_SETFONT) { use font specified in template } else if (DS_FIXEDSYS) { use GetStockFont(SYSTEM_FIXED_FONT); } else { use GetStockFont(SYSTEM_FONT); }
Notice that Once the dialog manager has the font, it is measured so that its dimensions can be used to convert dialog units (DLUs) to pixels. Everything in dialog box layout is done in DLUs. Here's a reminder if you've forgotten the formula that converts DLUs to pixels. In explicit terms: // 4 xdlu = 1 average character width // 8 ydlu = 1 average character height #define XDLU2Pix(xdlu) MulDiv(xdlu, AveCharWidth, 4) #define YDLU2Pix(ydlu) MulDiv(ydlu, AveCharHeight, 8) The dialog box size come from the template. cxDlg = XDLU2Pix(DialogTemplate.cx); cyDlg = YDLU2Pix(DialogTemplate.cy); The dialog size in the template is the size of the client area, so we need to add in the nonclient area too. RECT rcAdjust = { 0, 0, cxDlg, cyDlg }; AdjustWindowRectEx(&rcAdjust, dwStyle, hmenu != NULL, dwExStyle); int cxDlg = rcAdjust.right - rcAdjust.left; int cyDlg = rcAdjust.bottom - rcAdjust.top; How do I know that it's the client area instead of the full window including nonclient area? Because if it were the full window rectangle, then it would be impossible to design a dialog! The template designer doesn't know what nonclient metrics the end-user's system will be set to and therefore cannot take it into account at design time. (This is a special case of a more general rule: If you're not sure whether something is true, ask yourself, "What would the world be like if it were true?" If you find a logical consequence that is obviously wrong, then you have just proven [by contradiction] that the thing you're considering is indeed not true. This is an important logical principle that I will come back to again and again. In fact, you saw it just a few days ago. )
Assuming the POINT pt = { XDLU2Pix(DialogTemplate.x), YDLU2Pix(DialogTemplate.y) }; ClientToScreen(hwndParent, &pt);
But what if the caller passed
Moral of the story: Always pass a Okay, we are now all ready to create the dialog: We have its class, its font, its menu, its size and position... Oh wait, we have to deal with that subtlety of dialog box creation discussed earlier: The dialog box is always created initially hidden. BOOL fWasVisible = dwStyle & WS_VISIBLE; dwStyle &= ~WS_VISIBLE;
The dialog class and title come from the template. Pretty much everyone just uses the default dialog class, although I explained in an earlier article how you might use a custom dialog class. Okay, now we have the information necessary to create the window. HWND hdlg = CreateWindowEx(dwExStyle, pszClass, pszCaption, dwStyle & 0xFFFF0000, pt.x, pt.y, cxDlg, cyDlg, hwndParent, hmenu, hinst, NULL);
Notice that we filter
out all the low style bits (per-class) since we already
translated the
This is why your dialog procedure doesn't get
the window creation messages like // Set the dialog procedure SetWindowLongPtr(hdlg, DWLP_DLGPROC, (LPARAM)lpDlgProc); The dialog manager does some more fiddling at this point, based on the dialog template styles. The template may have asked for a window context help ID. And if the template did not specify window styles that permit resizing, maximizing or minimizing, the associated menu items are removed from the dialog box's system menu. And it sets the font. SetWindowFont(hdlg, hf, FALSE);
This is why the first message your dialog procedure
receives happens to be Okay, the dialog frame is now open for business. Next up: Creating the controls. |
Comments (16)
Comments are closed. |
Good God.
I think I’ll stick with my plain old msgbox(), thank you.
What hwndParent do you choose if the dialog is the main window (first created window) of your application?
Read the article again. Notice that the hwndParent is passed to CreateWindow. What hwndParent do you choose if you call CreateWindow for your main window?
GetDesktopWindow() ?
No, yoy can’t pass GetDesktopWindow(), you need to pass what malloc(0xFFFFFFFF) returns, see in "Writing Secure Code, 2nd ed", page 196.
Anyway, thank you Raymond for the articles. I’ve built my own Dialog Manager and I hope your current series can help me make it somewhat better.
BradC: A message box is a specialized dialog box, so it goes through all of the same stuff. However, all of this is implemented by Windows for you–I don’t see what the issue is.
I am trying to control features in the Parent Window from my Dialog Box. Do I have to set a variable or can I send a command. I am just trying to change colors and some text values in a Flight Simulation Trainer. I was not sure if I should just set a global variable and then refresh the Parent after the focus was off of the Dialog Window.
Really hoping this series of articles sheds some light on problem i currently have…
am adding dialog boxes as child windows to a CWnd-based window. Yet, there’s a problem with Tab- and arrow-keys navigation. Instead of stepping through all controls in the main window and included child dialog windows in a loop (like it happens when i add the child dialogs to ‘regular’ dialog) the keyboard focus becomes trapped in first child dialog window it encounters, looping then just through the controls of this child dialog and never getting out of it.
the main CWnd has WS_EX_CONTROLPARENT style, the child dialogs have the WS_CHILD, DS_CONTROL and the WS_EX_CONTROLPARENT styles as well… can’t figure out what should be added/ removed/ altered to get the focus behave just like for dialogs-in-dialog case, and what’s causing the current, odd behaviour in the first place. :/
Sorry, tolaris, that won’t be covered in this series. That’s going to have to wait until I feel like going in depth into IsDialogMessage.
Gah, just my luck =)
a call to PreTranslateInput() in the parent CWnd’s overriden PreTranslateMessage() was one of the things i tried, but given MSDN left me with very vague understanding of how the whole navigation thing is done, obviously that didn’t result in anything useful. Oh well, will just put this off ’till better times and work on other parts of functionality meantime…
I’m curious too:
What hwndParent do you choose if you call CreateWindow for your main window?
If I’m a new app, I don’t have an invocation point, so unless I sniff where the mouse is, wouldnt’ I always show up on the primary desktop?
For my main window, I’ve always just used NULL as the parent, and it seems to work just fine…
Aha !! Some time in 1992, while learning PM programming in Petzold for OS/2, I just wondered why dialogs don’t receive a WM_CREATE.
Today, 13 years later, I learned why…
Thanks Raymond!
but couldn’t they put the SetWindowLongPtr(hdlg, DWLP_DLGPROC, (LPARAM)lpDlgProc); in the WM_CREATE handler (or before) and call lpDlgProc directly for this message?
tolaris, this thread may help you out:
http://groups-beta.google.com/group/comp.os.ms-windows.programmer.controls/browse_frm/thread/476870c9fe039ab2/c7a54e1fca16eb94#c7a54e1fca16eb94
It specifies where the class name should be looked up.