[ Next ] [ Previous ] | Chapter 13 | [ Contents ] [ Chapter 12: Resources ] [ Chapter 14: Menus ]

### Dialog Boxes

Dialog boxes are designed to gather specific pieces of information from the user. Dialog contain a mix and match of child control windows. A window that pops up and contains such fields as "Name:", "Address", "Phone", "City", and "State", is a good example of a dialog box. |

There are three ways to create a dialog box and its child controls - by using a resource file, by physically calling the WinCreateWindow for the dialog box and each of its controls, or by using WinCreateDlg. The resource file is the easiest way to crease a dialog box. The Dialog Box Editor shipped with the Toolkit is designed to help facilitate this creation process.

Dialog boxes come in two styles - modal and modeless. A modeless dialog box lets the user interact with all the other windows and controls belonging to the same process. A modal dialog box is more restrictive of the user's input. A user cannot interact wish the other windows and controls that are children of the owner of the dialog box, including the owner. A modal dialog box is designed to be used when the user is required to enter some information before proceeding on to the next step in the application.
The following sample program is designed to introduce dialog box programming and to display the difference between modal and modeless dialog boxes.

DIALOG.C DIALOG.RC DIALOG.H DIALOG.MAK DIALOG.DEF | Dialog.exe - Modeless dialog example

The resource file, DIALOG.RC, is the starting point for the sample program. Two items are defined in the file, a menu and the dialog box. The resource file for the window shows the menu that we would like displayed in our client window. For more information on resources, see Chapter 12.

#### The Dialog Box Template

The following is the resource definition to create the dialog boxes used in the DIALOG.C program.

> `DLGTEMPLATE IDD_DIALOG LOADONCALL MOVEABLE DISCARDABLE`
> `{`
> `DIALOG "Dialog example", IDD_DIALOG, 53, 28, 260, 55,`
> `WS_VISIBLE,`
> `FCF_SYSMENU | FCF_TITLEBAR`
> `{`
> `LTEXT "?", IDT_DIALOGNAME, 10, 40, 150, 8`
> `LTEXT "?", IDT_CLICK, 10, 30, 150, 8`
> `DEFPUSHBUTTON "OK", DID_OK, 10, 10, 50, 13`
> `}`
> `}`

The dialog IDD_DIALOG is created in the resource file as visible, with a system menu and title bar.
The next step is to define the controls that are to appear on the dialog box In this example only an "OK" pushbutton and some static text will be used. The IDT_CLICK text will be used to communicate some instructions to the user. The IDT_DIALOGNAME is used to specify whether this is a modal or modeless dialog box.

#### The Client Window Procedure

The client window procedure, ClientWndProc, is not very big. A window word is used to store some information that we will need later in the dialog procedure. This information is stored in a DLGINFO structure. The structure includes the structure size, a BOOL variable to indicate whether the user selected modal or modeless from the menu, the handle of the modeless dialog box, and the handle of the client window. This structure is allocated in the WM_CREATE processing, and cleanup is done in the WM_DESTROY processing.
The programmatic differences between a modal and nonmodal dialog box exist in the processing of the WM_COMMAND message.
In our WM_COMMAND processing, we first find out who is sending us the WM_COMMAND message. The resource ID for the sender is located in mpParm1. If the user selected "Modal Dialog Box", IDM_MODAL is returned in mpParm1. A Boolean variable, pDlglnfo->bModal, is used to indicate to the DlgProc whether the user selected a modal or modeless dialog box.

#### Creating a Modal Dialog Box

The function WinDlgBox is used to create a modal dialog box.

> `ULONG APIENTRY WinDlgBox(HWND hwndParent,`
> `HWND hwndOwner,`
> `PFNWP pfnDlgProc,`
> `HMODULE hmod,`
> `ULONG idDlg,`
> `PVOID pCreateParams);`

When WinDlgBox is used to create a dialog box, a message queue is created for that dialog. User interaction with the other message queue (and the client window associated with it) is held up until the dialog box is dismissed and the message queue is destroyed.

> `pDlgInfo->bModal = TRUE;`

> `WinDlgBox(HWND_DESKTOP,`
> `hwndWnd,`
> `DlgProc,`
> `NULLHANDLE,`
> `IDD_DIALOG,`
> `pDlgInfo);`

The first parameter is the parent, HWND_DESKTOP, and the second parameter is the owner window, hwndWnd. The programmer almost always will want to specify the desktop as the parent of a modal dialog, and the client window as the owner. If the frame or client was specified as the parent of the dialog, the frame window would still be active, thus preventing the whole purpose of using a modal dialog. The third parameter is the pointer to the dialog process function, in this case DlgProc. NULLHANDLE tells the system that the resources for the dialog process, DlgProc, are located in the .EXE file. IDD_DIALOG is the resource ID for the dialog. The last parameter is the data area. This is used to pass programmer - defined data of type PVOID into the dialog procedure. In this area we will pass a pointer to our dialog information structure, pDlgInfo. WinDlgBox is actually a combination of four functions, WinLoadDlg, WinProcessDlg, WinDestroyWindow, and return.

| Gotcha! The last parameter to WinDlgBox must be a pointer. This parameter undergoes a procedure called "thunking" that converts a 32-bit pointer into a pointer that is readable by 16'bit code. The application will trap if the value is not a pointer and the system attempts to thunk it. The dialog box functions are l6-bit in OS/2 2.1, and must try and thunk this value. The dialog box functions in Warp are 32-bit, so no thunking will be done; however, if previous versions of the operating system must be supported, it is best to be prepared for thunking.

#### Creating a Modeless Dialog Box

> `pDlgInfo->bModal = FALSE;`
> `if (!pDlgInfo->hwndModeless)`
> `pDlgInfo->hwndModeless = WinLoadDlg(HWND_DESKTOP,`
> `hwndWnd,`
> `DlgProc,`
> `NULLHANDLE,`
> `IDD_DIALOG,`
> `pDlgInfo);`
> `else`
> `WinSetWindowPos(pDlgInfo->hwndModeless,`
> `HWND_TOP,`
> `0,`
> `0,`
> `0,`
> `0,`
> `SWP_SHOW|SWP_ACTIVATE);`

In this example, we first set the bModal variable to FALSE to indicate that this will be a modeless dialog box.

| Gotcha! A modeless dialog is not destroyed by WinDismissDlg, only hidden. In order to destroy the dialogs loaded by WinLoadDlg, WinDestroyWindow must be called implicitly for each modeless dialog that has been created.

If the user selects the modeless option from the menu multiple times, we do not create the same dialog over and over; instead, we just check to see if its already exists. If the window handle is there, WinSetWindowPos is used to show the dialog and make it the active window.
WinLoadDlg is used to create a modeless dialog box, and this function returns immediately after creating it. WinDlgBox waits until it finishes its processing before returning. This is why a modeless dialog box permits user interaction with the other windows and a modal dialog box does not. The parameter list for WinLoadDlg is exactly the same as for WinDlgBox.

#### The Dialog Procedure DlgProc

The dialog procedure, in this case DlgProc is fairly similar to a window procedure. Our program can use the same dialog process for both the modal and modeless dialog boxes.

| Gotcha! One difference between a dialog procedure and a window procedure is the default procedure function. A dialog procedure must call WinDefDlgProc instead of WinDefWindowProc. If a dialog procedure behaves irrationally, it should be checked to see if it includes WinDefDlgProc. These two functions often get interchanged.

One of the other differences between dialog and window procedures is the appearance in the former of the WM_INITDLG message instead of the usual WM_CREATE. This message is provided to give the programmer a place to put the initialization code for the dialog box.

> `pDlgInfo = PVOIDFROMMP(mpParm2);`

The first thing we do is retrieve the information sent to us through the WinLoadDlg or WinDlgBox function. Both these functions will send this information in the message parameter 2 of the WM_INITDLG message.

> `WinQueryWindowRect(pDlgInfo->hwndClient,`
> `&rclClient);`

> `lHeight = rclClient.yTop-rclClient.yBottom;`
> `lWidth = rclClient.xRight-rclClient.xLeft;`

In order to make our dialog program prettier, we'll position the two dialogs directly on the client window. However, the parent of the dialogs is the desktop, and remember, the children wilt be positioned relative to the parent. So we do some math. First, we find the height and width of the client area, and use these dimensions to see where the dialogs should be placed relative to the client. We'll start the dialogs at the x coordinate that is 1/8th of the client area width. The y coordinate will differ depending on whether the dialog is the modal dialog or the modeless dialog.

> `ptPoints.x = lWidth/8;`
> `ptPoints.y = bModal?lHeight/19:lHeight/19*10;`

Now that we know where we would put our dialogs if they were placed relative to the client window's coordinate system, all we have to do is find where these coordinates are on the desktop window. And Presentation Manager has a function that will do this for us: WinMapWindowPoints.

> `BOOL APIENTRY WinMapWindowPoints(HWND hwndFrom,`
> `HWND hwndTo,`
> `PPOINTL aptlPoints,`
> `LONG lCount);`

hwndFrom is the handle of the window to map the coordinate space from. hwndTo is the handle of the window to map the coordinate space to. aptlPoints is a point to an array (one or more) of POINTL structures that on input contain the coordinates to map and on output contain the new coordinates relative to hwndTo. lCount is the number of structures in the aptlPoints array
In our case, the function looks like this.

> `WinMapWindowPoints(pDlgInfo->hwndClient,`
> `HWND_DESKTOP,`
> `&ptPoints,`
> `1);`

On the function's return, ptPoints will contain the new x and y coordinates relative to the desktop. We use these coordinates as the basis for the WinSetWindowPos function to adjust the size and position of the dialog.

> `WinSetWindowPos(hwndDlg,`
> `NULLHANDLE,`
> `ptPoints.x,`
> `ptPoints.y,`
> `lWidth/8*6,`
> `lHeight/19*8,`
> `SWP_MOVE|SWP_SIZE);`

The WM_COMMAND processing is just like the WM_COMMAND processing for the client window. If the user presses the OK pushbutton, the dialog box is canceled with WinDismissDlg.

#### WM_COMMAND and Dialogs

Some "features" (actually they really can be nice) can cause problems in the future if programmers are unaware of the way WinDefDlgProc handles WM_COMMAND messages. A dialog will be dismissed if a WM_COMMAND message is passed to WinDefDlgProc. In some cases, this makes sense. For instance, if the user presses the OK or CANCEL pushbuttons, it would be perfectly logical for the dialog box to go away. However, if the other pushbuttons exist, and the programmer does nor want the dialog box to be dismissed, WM_COMMAND processing must be intercepted and return FALSE, instead of letting the message processing fall through to WinDefDlgProc. This also means that WinDismissDlg must be called when the programmer is ready for the dialog to disappear and WinDestroyWindow when he or she is ready to destroy the dialog box.
WinDismissDlg is also called if a WM_QUIT message is sent to the dialog.

#### Summary

Dialogs will become an integral part of most of a programmer's Presentation Manager programs. They are easy to use and provide a clean user interface. The main drawback to dialogs is the lack of true device - independent dialog coordinates. Currently, a set of multiple dialogs must be created for different screen resolutions.

[ Next ] [ Previous ] | Chapter 13 | [ Contents ] [ Chapter 12: Resources ] [ Chapter 14: Menus ]