Windows Programming/Dialog Boxes

People are familiar with dialog boxes. They are the grey windows that pop up on Windows systems to display messages, and allow the user to set parameters. There are 3 types of dialog boxes: modeless, modal, and system modal.

Modal
Modal dialog boxes are generally used inside a program, to display messages, and to set program parameters. Modal dialog boxes come to the front of the screen, and you may not use the program while the modal dialog box is open. to continue using the program, the modal dialog box must be closed.
System Modal
System modal dialog boxes are like modal boxes, except that they supersede the entire desktop area. When a system modal dialog box is open, nothing else on the screen can be clicked or selected.
Modeless
Modeless dialog boxes are able to be deselected, and control can be taken away from a modeless dialog box and transferred to some other window. Modeless dialog boxes are frequently used as a fast and easy way to create a window, without having to register a window class. Modeless dialog boxes are common in the Windows control panel.

MessageBox

edit

The most simple type of dialog box is the MessageBox function. The MessageBox function takes 5 parameters: a handle to a parent, a message, a title, and an option. If the parent handle is NULL, the message box is modeless. If you provide a handle for a parent window, the MessageBox can become Modal to the parent window.

MessageBox dialog boxes have a number of different options that can be specified: Button types, Icons, modality (modal/modeless), and text justification. These options are specified as bit flags, that can be used by bitwise ORing them together.

Buttons

edit

Message boxes can have standard OK or Cancel buttons, or they can have a "Yes, No, Cancel" configuration, or a number of derivatives. Only one primary button scheme can be used per message box:

  • MB_ABORTRETRYIGNORE: The message box contains three push buttons: Abort, Retry, and Ignore.
  • MB_CANCELTRYCONTINUE: Same as MB_ABORTRETRYIGNORE, but preferred on Windows 2000/XP.
  • MB_OK: The message box contains an "OK" button. This is the default.
  • MB_OKCANCEL: The message box contains two push buttons: OK and Cancel.
  • MB_RETRYCANCEL: The message box contains two push buttons: Retry and Cancel.
  • MB_YESNO: The message box contains two push buttons: Yes and No.
  • MB_YESNOCANCEL: The message box contains three push buttons: Yes, No, and Cancel.

To display an icon in the message box, specify one of the following values. In addition, a message box can add an additional "Help" button by specifying the "MB_HELP" flag. A "Default Button", a concept that we will see frequently in this chapter, is the button that is automatically selected when a dialog box is opened. Windows provides the ability to set the default button to any of the buttons on a message box, by using the MB_DEFBUTTONx macro. Here is an example:

MessageBox(NULL, "This is a Test", "Test", MB_OKCANCEL|MB_HELP|MB_DEFBUTTON2);

This will have a message box with an "OK", a "Cancel", and a "Help" button, and the "Cancel" button will be automatically selected.

Icons

edit

A message box may have no icons, or it may have one. You shouldn't specify a message box to have multiple icons. The different icons, according to MSDN are:

  • MB_ICONEXCLAMATION: An exclamation point icon appears in the message box.
  • MB_ICONWARNING: An exclamation point icon appears in the message box.
  • MB_ICONINFORMATION: An icon consisting of a lowercase letter i in a circle appears in the message box.
  • MB_ICONASTERISK: An icon consisting of a lowercase letter i in a circle appears in the message box.
  • MB_ICONQUESTION: A question mark icon appears in the message box.
    The question mark message icon is no longer recommended because it does not clearly represent a specific type of message and because the phrasing of a message as a question could apply to any message type. In addition, users can confuse the message symbol question mark with Help information. Therefore, do not use this question mark message symbol in your message boxes. The system continues to support its inclusion only for backward compatibility.
  • MB_ICONSTOP: A stop sign icon appears in the message box.
  • MB_ICONERROR: A stop sign icon appears in the message box.
  • MB_ICONHAND: A stop sign icon appears in the message box.

Modality

edit

Finally, the MessageBox can be defined as being Modal, Modeless, or System Modal, by using another identifier: MB_APPLMODAL, MB_SYSTEMMODAL, or MB_TASKMODAL. MB_APPLMODAL is the default value, and only works if a parent window handle was specified to the function. There are a number of other options available, check out MSDN for more details.

Dialog Box Procedures

edit

Dialog box procedures are slightly different from window procedures. Specifically, they return BOOL values, instead of LRESULT values. Also, dialog boxes do not have a default message processing function, because messages don't always need to be handled. Specifically, Windows manages dialog boxes, and Windows will handle the unused messages. If a dialog box processes a certain message, it should return TRUE. If the message is not processed, the function should return FALSE. Also, Dialog boxes do not get a WM_CREATE message, but instead get a WM_INITDIALOG message. Furthermore, when a dialog box has finished its business, it should call the EndDialog function.

Here is an example of a skeleton dialog box function:

BOOL CALLBACK MyDlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
{
   switch(msg)
   {
       case WM_INITDIALOG:
           return TRUE;
       case WM_DESTROY:
           EndDialog(hDlg, 0);
           return TRUE;
   }
   return FALSE;
}

Creating Modal Dialog Boxes

edit

Once a dialog box procedure has been defined, a dialog box can be created by calling either the DialogBox or DialogBoxParam function. These functions return an NRESULT value, that is the integer number passed to the EndDialog function in the dialog box procedure.

The DialogBox function will not return until the dialog box is closed. This means, essentially, that the program is frozen in time until we close the dialog box. The DialogBox function requires 2 handles: the module instance handle and the handle of the parent window. Also, the DialogBox function requires that a string be passed naming the resource where the dialog box is defined. The last argument to DialogBox is a pointer to the dialog box procedure function, that you have already defined.

To pass a parameter to a dialog box, the function DialogBoxParam can be used. DialogBoxParam has all the same parameters as the regular version, except it takes a fifth argument as a 32-bit pointer. This 32 bit value will be passed as the LPARAM element of the WM_INITDIALOG message.

Indirect Dialog Boxes

edit

DialogBox and DialogBoxParam both require that the dialog box be defined in a resource. However, if you want to make the dialog box on the fly, you can use the DialogBoxIndirect or the DialogBoxIndirectParam functions. When defining a dialog box indirectly, we need to fill out a DLGTEMPLATE structure, and pass a pointer to that structure to the function, in place of a resource identifier. The DLGTEMPLATE contains fields for determining some of the characteristics of the dialog box, such as the dimensions and screen location.

The DLGITEMTEMPLATE structure is used to define individual dialog box items. For more information on this subject, search MSDN.

Creating Modeless Dialog Boxes

edit

Modeless dialog boxes are a breed of a different color, and are more like windows than dialog boxes. First, we need to modify the message loop, to ensure that dialog box messages are routed correctly:

while(GetMessage(&msg, NULL, 0, 0))
{
   if(!IsDialogMessage(hDlg, &msg))
   {
       TranslateMessage(&msg);
       DispatchMessage(&msg);
   }
}

Now, there are 2 ways we can define a message box in a resource script, with a class or without. We will discuss each in turn.

Without Class

edit

We can define a dialog box in a resource script with the DIALOG keyword. The resource will have an ID associated with it (either a number or a string), and this ID can be passed directly to the CreateDialog function.

With Class

edit

If we want to define a modeless dialog box in terms of a window class, we can use a few additions to make the job easier. First, we create a WNDCLASS structure with the information about our dialog box. However, there is one difference, in that we must set the cbWndExtra field to the value DLGWINDOWEXTRA value:

wnd.cbWndExtra = DLGWINDOWEXTRA;

Then, we register the class like normal. Since we are registering our window classes like normal windows, it shouldn't come as a surprise that Modeless dialog boxes use a regular window procedure, and not a dialog box procedure. Now, Windows identifies classes by name, so we should remember the name of our class. Let's say we named our class "MyDlgClass". We could create a dialog box resource as such:

MYDLGCLASS DIALOG DISCARDABLE 100, 100, 200, 200
CAPTION "My Dialog Box"
CLASS "MyDlgClass"
FONT 8, "MS Sans Serif"
BEGIN
...
END

Notice the field that says "CLASS"? This is the same string that we used in our WNDCLASS structure to name the class. It is important that these two strings be identical, because Windows needs this string to link the WNDCLASS and the dialog box resource together. Notice also that we used the string "MYDLGCLASS" to identify the dialog resource. This isn't mandatory, but it does make things convenient later on.

Now, instead of calling CreateWindow, we will call the easier-to-use function CreateDialog. We do not use the DialogBox function, because CreateDialog returns immediately, and doesn't halt program execution.

Here is an example:

HWND hDlg;
hDlg = CreateDialog(hInst, "MyDlgClass", hwndParent, MyDlgProc);

Here, we are saying that "hInst" is the instance handle of the application, and "hwndParent" is the handle to the parent window of our dialog box. If the hwndParent parameter is NULL, the dialog box won't have a parent. When the modeless dialog box is finished, it calls "DestroyWindow", not "EndDialog", like a modal dialog box would.

Common Dialog Boxes

edit

The Common Dialogs is a library of functions that automatically produce some of the most common dialog boxes in Windows. This is an effort to make some amount of continuity between different programs, so that each different program doesn't create its own proprietary "File Open" dialog, for instance.

Each Common Dialog generally has a single function that takes a pointer to a structure. This structure is defined specifically for each different control. The common controls can be added to a project by including the <commdlg.h> header file, and linking to the comdlg32.dll library.

Some of the common controls available through this library are the "Choose Font" dialog box, the "File open" and "File save" boxes, and the "Color Palette" dialog box.

MSDN

ChooseColor

edit

The ChooseColor function brings up the color palette window, and returns a 32-bit color value to your program.

BOOL ChooseColor(LPCHOOSECOLOR lpcc);

ChooseColor takes a single argument, in the form of a pointer to a CHOOSECOLOR structure. This structure is initialized with various values, and when the function returns, the CHOOSECOLOR structure contains the color value code.

GetOpenFileName and GetSaveFileName

edit

These two functions bring up the familiar file open and file save dialog boxes that are found in nearly every Windows application.

BOOL GetOpenFileName(LPOPENFILENAME lpofn);
BOOL GetSaveFileName(LPOPENFILENAME lpofn);

Both of these functions take a pointer to an OPENFILENAME structure. This structure controls such things as the file extensions that may be loaded, and the starting path to look in. When the function returns, the structure will contain the name of the file selected. Once your program has this information, you can use the File I/O API to access the file.

ChooseFont

edit

The ChooseFont function brings up a familiar dialog box that allows the user to select a font and various font attributes such as size, underline/bold/italics, color, etc. This function takes a pointer to a CHOOSEFONT structure.

BOOL ChooseFont(LPCHOOSEFONT lpcf);

Dialog Box Resources

edit

Dialog boxes can be specified in a resource script, to handle the tricky task of creating all the various child windows (buttons and editboxes, etc.) that a dialog box may contain. This process is described in detail in the Resource Script Reference in the appendix. Here, we will discuss some of the basics of using a resource script to define a dialog box.

The DIALOG keyword

edit

A dialog box resource is specified with the DIALOG (must be all caps) keyword. The DIALOG keyword is preceded by the resource identifier, and is followed by a series of dimension values:

ID_DLGBOX DIALOG X, Y, CX, CY

X and Y are the location coordinates of the upper-left corner of the dialog box, in relation to the upper-left corner of the screen. Remember, all the coordinates start at (0,0) in the upper-left hand corner. The next set of numbers, CX and CY, are the dimensions of the dialog box. These dimensions do not include the title bar (if any), so setting your Y value to 0 will make a dialog box that is only a title bar.


      +---------------> X
  
      ["DialogBox"   [_][O][x]]
  +   |                       |
  |   |                       |
  |   |                       |
  |   |                       |
  |   |                       |
  |   |                       |
  v   |                       |
  Y   |                       |
      |                       |
      |                       |
      |                       | 
      +-----------------------+

After the DIALOG declaration, there are a number of other fields that can be filled in, to provide information about your dialog box:

ID_DLGBOX DIALOG 100, 100, 200, 150
STYLE WS_OVERLAPPED | WS_CAPTION | WS_VISIBLE
CAPTION "Title Bar Text"
FONT 8, "MS Sans Serif"

The STYLE declaration contains all the window styles, bitwise OR'd, that you would have used in the WNDCLASS structure, or in the style field of the CreateWindow function. All the same values are available. The CAPTION is the title of the dialog box. The FONT is the point-size and the TrueType font to be used on all the surfaces of the dialog box. Any font and size can be specified, although if the font is too big, your dialog box will be very annoying.

Now, once we have our dialog box sized and shaped the way we want it, we can start to fill it with control buttons and edit boxes, and all sorts of other goodies. First, we use the BEGIN and END tags:

ID_DLGBOX DIALOG 100, 100, 200, 150
STYLE WS_OVERLAPPED | WS_CAPTION | WS_VISIBLE
CAPTION "Title Bar Text"
FONT 8, "MS Sans Serif"
BEGIN
  ...
END

Next, we can start to fill in the dialog box with buttons, checkboxes, or whatever we want, using the following format:

ID_DLGBOX DIALOG 100, 100, 200, 150
STYLE WS_OVERLAPPED | WS_CAPTION | WS_VISIBLE
CAPTION "Title Bar Text"
FONT 8, "MS Sans Serif"
BEGIN
  PUSHBUTTON "OK",  IDOK,      10, 10, 50,  15, WS_TABSTOP
  CHECKBOX "Box 1", IDC_CB1,   10, 30, 50,  15, WS_TABSTOP 
  EDITTEXT          IDC_EDIT1, 10, 50, 100, 100
END

After the declaration, you may optionally include one or more style flags, to specify how you want a particular control to appear. The WS_TABSTOP identifier specifies which controls can be selected when you press the TAB key on the keyboard. When you press the TAB key, control switches among the dialog box controls in the same order that they are specified in the resource script (top to bottom).

Next Chapter

edit