Content-Length: 13424 Content-Type: text/html Dialogs as Main Windows

Dialogs as Main Windows

Lots of people ask about how to use dialog windows as main windows. The reasons are obvious, such as being able to draw the windows instead of doing time consuming positioning calculations and creating them on the fly. Also, you get the benefit of having the window sized to fit the default system font on the target hardware. A main window dialog is definitely doable, and it's pretty easy. But there are a number of different approaches to do it, each of which has its own pros and cons.

Everyone has used WinDlgBox() to run dialogs but, to understand the best way to use dialogs as main windows, you need to understand what goes on under the hood when you call it. Basically two APIs, WinLoadDlg() and WinProcessDlg() make up the guts of WinDlgBox(), with some extra housekeeping in between. So first a quick overview of what happens when you create a dialog.

How Dialogs Work

The WinLoadDlg() API uses DosGetResource() to load up the dialog description of your dialog (see PM Resources and Resource Ids below) from the resources left in the DLL or EXE by the resource compiler. It uses this description to create a frame window and all of the individual controls defined by the dialog. The controls are created in the same order as you put them in the dialog, which becomes important below. Once everything is created it will subclass the frame using your dialog procedure (see Subclassing and Superclassing below) and send you a WM_INITDLG message.

Once done with all of that, WinLoadDlg() destroys the dialog definition buffer and returns. Note that it only creates the windows and gets everything set up but nothing else. The frame was left invisible so you don't see it yet (unless you showed it during WM_INITDLG processing.) At this point, the frame window is no different than if you had manually created it, and its controls, yourself. So, as far as windows go, a dialog is very literally a frame window. It is only special in the fact that it is subclassed by a cooperative party (you) and that it is processed differently. You are the cooperative party in that you voluntarily pass any unhandled messages to WinDefDlgProc(). Much of the magic of dialogs is really in WinDefDlgProc(). It provides the standard dialog style responses to incoming user and system input. If you did not pass messages to WinDefDlgProc() then you would not get any of the standard dialog reactions.

Modal Message Loops

When a dialog is being processed, you've probably noticed that it will not let any input go to the other windows of the program. It is in a 'modal message loop'. When you run a dialog, your message loop is no longer being used. Instead, a message loop inside PM is getting input. This gives it control over where user input messages go to or if they are allowed to go anywhere at all, since input messages are always posted and therefore come in via a message loop) .

The WinProcessDlg() call is what provides this modal loop. It does the usual WinGetMsg() and WinDispatchMsg() calls, but also watches for dialog specific circumstances such as the dismissal of the dialog. WinDismissDlg() does two things, it stores the caller's return code parameter in the frame's extra window word (at QWS_RESULT) and sets the FF_DLGDISMISSED flag in the frame's QWS_FLAGS window words. WinProcessDlg() will check the dismiss flag and see that its time to return. So it will pull the result code from the window's extra data and return it to the caller as the result code of the dialog.

Theoretically, you could clear the dismissed flag and call WinProcessDlg() again to start up the dialog again. But it would look ugly because the dismiss would hide the dialog and then it would reappear again. I have not tried it myself, but you can give it a try if you think it would be useful.

Also, you could theoretically create a frame, and any desired control windows within it, subclass it to a dialog procedure and then call WinProcessDlg() to process it modally as a dialog. WinProcessDlg() probably wouldn't know or care that the windows were created manually instead of as a dialog resource. Just be sure that you subclass it to a window procedure that is written like a dialog procedure, i.e. it uses WinDismissDlg() and passes unhandled messages to WinDefDlgProc().

WinProcessDlg() does not destroy the window when it returns, but it does make it invisible. If you use the separate load and process steps yourself, then you must destroy the dialog frame. If you called WinDlgBox() it will destroy the frame before it returns, and it will pass on back to you the return that came back from WinProcessDlg().

The Easy Way Out

The easiest way to have a dialog as a main window is to just do the usual WinInitialize() and WinCreateMsgQueue(), then use WinDlgBox() to run a dialog. When it returns, terminate and exit. Having the dialog do all the work is nice, but it is also the reason why this is sometimes not the best way to do a main window as a dialog. For starters you don't have a lot of control and some of the standard dialog processing is not really appropriate for main windows. For instance:

The Next Step

The next step up the ladder of complexity vs flexibility is to use the WinLoadDlg() API to load up the dialog and then enter your own message loop, so that you are not processing it modally. As discussed above there is nothing magic about a dialog window itself, i.e. using WinLoadDlg() is not much different from you creating the frame and controls yourself, or at least the results are not. But once you move to this non- modal dialog style, you are not really a dialog any more, so you don't want to keep calling WinDefDlgProc() or otherwise acting like you are still a dialog when you are not. You can still pretty much get away with such things but you might run into some interesting problems. For instance, if you just dismiss the dialog on a WM_COMMAND (as a normal dialog would), you will end up with a hung program (from the user's perspective.) The dialog will be hidden but the window will not be destroyed and no WM_QUIT will be issued to the message queue to cause the program to exit. There are some other small problems of that sort, that are liveable but can bite you or confuse your users.

What I suggest is this. Use WinQueryClassInfo() to query the class info for the WC_FRAME class. Remember that the class name for a standard PM controls is not a real text string but a magic pointer, so the name you pass to WinQueryClassInfo() is in the form "#0000x", where x is the number from the defined WC_FRAME value (look in PMWin.h). This information structure has the address of the original frame class' window procedure. If you now pass unhandled messages to that procedure instead of WinDefDlgProc(), you will effectively bypass the dialog specific processing and turn your dialog into a regular subclassed frame window. Keep in mind that you should still handle WM_INITDLG to do any setup if you want to, but you will not see WM_CREATE because the window is already created before it is subclassed to your dialog procedure. If you now just get rid of WinDismissDlg() and you have a pretty normal frame window. If you don't need the subclass anymore, for instance because your dialog has a client window, you can call WinSubclassWindow() with the original frame window class. This will get rid of your subclass and save processing effort in the process, since every frame message does not have to go through two layers before it gets handled. I haven't tried it but you could possibly pass the original frame procedure as the dialog procedure and not have to have a dialog proc at all? You try it though and let me know, and I will use it if it doesn't waste your partition table or anything. (I'm so brave. [grin])

As an aside, WinDefDlgProc() is really doing the same thing itself internally, handling some messages and passing others on to the original frame procedure. You are just skipping the middle man by doing this yourself.

Though this is the most technically correct approach, it does have one down side. You loose the automatic tabbing support that WinDefDlgProc() provides. This though is very easily dealt with. See Dialog User Interaction below for a discussion of this issue.

The Ultimate Control

The ultimate control of dialogs is accomplished by manually parsing the dialog resource and creating the controls yourself. That is a pretty large subject so I will not cover it here. However you might want to read my article in the July/Aug 1994 issue of 'OS/2 Developer', which covers this subject pretty well. Given the information in this article and that one, you should be able to handle it with a little experimentation. This knowledge is also useful for creating your own dialog editor if you care to do such things.

I use this method in my CIDLib class libraries, which allows the TDialogWnd class to be very expandable. Its constructor provides a callout (which is called for each new control parsed from the resource) so that the calling code can optionally override the type or attributes of each control. It's a pretty powerful and flexible system, but without all the work of providing your own screen builder (though I will probably do that eventually.) Dialog User Interaction

For various reasons, people often want to understand how tabbing in dialogs works. Sometimes, as in the previous section, they've loaded a complex dialog as a main window and want to retain the tabbing capability. Other times, they have a similar complex set of controls that they have created manually and want to provide tabbing for them. This section provides some background on how tabbing works and tells you how to easily provide it for yourself.

Copyright Notice | Reply to Author | Search | Section Index | Previous Page | Next Page

If you see a problem with this web site, contact edm2_eds@iqpac.com.