Differences Between Modal and Modeless Dialog Boxes

Working with modeless dialog boxes is similar to working with modal dialog boxes, but there are several important differences:

Modeless dialog boxes usually include a caption bar and a system menu box. The STYLE statement in the dialog box template for a modeless dialog box will look something like this:

STYLE WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_VISIBLE

The caption bar and system menu allow the user to move the modeless dialog box to another area of the display using either the mouse or the keyboard. You don't normally provide a caption bar and system menu with a modal dialog box, because the user can't do anything in the underlying window anyway.

Note that the WS_VISIBLE style is included in our sample STYLE statement. If you omit WS_VISIBLE, you must call ShowWindow after the CreateDialog call:

hDlgModeless = CreateDialog ( . . . ) ;

ShowWindow (hDlgModeless, SW_SHOW) ;

If you neither include WS_VISIBLE nor call ShowWindow, the modeless dialog box will not be displayed. In overlooking this fact, programmers who have mastered modal dialog boxes often experience difficulties when they first try to create a modeless dialog box.

Unlike messages to modal dialog boxes and message boxes, messages to modeless dialog boxes come through your program's message queue. The message queue must be altered to pass these messages to the dialog box window procedure. Here's how you do it: When you use CreateDialog to create a modeless dialog box, you should save the dialog box handle returned from the call in a global variable (for instance, hDlgModeless). Change your message loop to look like this:

while (GetMessage (&msg, NULL, 0, 0))

{

if (hDlgModeless == 0 || !IsDialogMessage (hDlgModeless, &msg))

{

TranslateMessage (&msg) ;

DispatchMessage (&msg) ;

}

}

If the message is intended for the modeless dialog box, then IsDialogMessage sends it to the dialog box window procedure and returns TRUE (nonzero); otherwise, it returns FALSE (0). The TranslateMessage and DispatchMessage functions should be called only if hDlgModeless is 0 or if the message is not for the dialog box.If you use keyboard accelerators for your program's window, then the message loop looks like this:

while (GetMessage (&msg, NULL, 0, 0))

{

if (hDlgModeless == 0 || !IsDialogMessage (hDlgModeless, &msg))

{

if (!TranslateAccelerator (hwnd, hAccel, &msg))

{

TranslateMessage (&msg) ;

DispatchMessage (&msg) ;

}

}

}

Because global variables are initialized to 0, hDlgModeless will be 0 until the dialog box is created, thus ensuring that IsDialogMessage is not called with an invalid window handle. You must take the same precaution when you destroy the modeless dialog box as explained below.

The hDlgModeless variable can also be used by other parts of the program as a test of the existence of the modeless dialog box. Other windows in the program can send messages to the dialog box while hDlgModeless is not equal to 0.

Use DestroyWindow rather than EndDialog to end a modeless dialog box. When you call DestroyWindow, set the hDlgModeless global variable to 0.

The user customarily terminates a modeless dialog box by choosing Close from the system menu. Although the Close option is enabled, the dialog box window procedure within Windows does not process the WM_CLOSE message. You must do this yourself in the dialog box procedure:

case WM_CLOSE :

DestroyWindow (hDlg) ;

hDlgModeless = 0 ;

break ;

Note the difference between these two window handles: The hDlg parameter to DestroyWindow is the parameter passed to the dialog box procedure; hDlgModeless is the global variable returned from CreateDialog that you test within the message loop.

You can also allow a user to close a modeless dialog box using push buttons. Use the same logic as for the WM_CLOSE message. Any information that the dialog box must ”return“ to the window that created it can be stored in global variables.