Working With Menus And Dialog Boxes by (23 December 1998) | Return to The Archives

Introduction

Aside from general application software, menus and dialogs are used from time to time in games/demos for things like configuration screens and tools, and at the same time they aren't very difficult to work with at all. If you read the previous tutorial, the concepts in this document should be easy to understand because we already went over them. So here we are, ready to step a little deeper into windows programming. Again, I'll be using Microsoft's Visual C++ compiler -- this time being a little more relevant because I'll be using the resource editor as well.

Menus

In case you honestly don't know what a menu is in Windows, perhaps this document is not for you (in fact its definitely not). Every Windows user probably knows what a menu is and uses a variety of them on a regular basis. But how do we program one? If you recall from the previous tutorial, everything translates down to a message that can be processed through our window procedure. This is true even for menu item clicks. Lets take a look at how to add a menu to a program such as the simple application from the previous tutorial. I won't actually be adding it for you -- that's left as an excercise for the reader.
The screenshot above shows VC's menu editor using the IDE, which translates to the following in the resource file:

     IDR_MAINMENU MENU DISCARDABLE
     BEGIN
         POPUP "&File"
         BEGIN
             MENUITEM "&Exit", ID_EXIT
         END
     END

Note that you do not have to use the menu editor to create your menus; in fact you don't even need to use a resource file at all. A resource file is a file that holds information about 'resources' such as menus, dialog windows, controls (such as edit boxes), and other things of this nature. In other words, it makes life a lot easier to use a resource editor that's part of a decent IDE. That way you can keep all of your resources easy to manage and easy to design. A big part of almost any program is aesthetic appeal ;]. If you elect not to use the resource editor, you can create your menus manually using functions like CreateMenu, AppendMenu, InsertMenuItem, and InsertMenu. You can also create and track pop-up menus using CreatePopupMenu and TrackPopupMenu.
For this document, I'll explain how to use the menus from a resource file such as that above. Basically all you need to do is go back to your WNDCLASS setup before registering your window and change one line as follows and be certain to include your resource file in your project:
wClass.lpszMenuName = MAKEINTRESOURCE(IDR_MAINMENU);
Now when you run your program, your window should have the menu shown below:
All that's left is to actually do something when the 'Exit' menu item is selected. That's not difficult at all. When a menu item is clicked (amongst other events such as button clicks), a "WM_COMMAND" message occurs and can be processed from within the window procedure. Basically all you need to do is add the following code to the window procedure from the previous tutorial:

    // Inside of our window procedure;

          switch(msg) {

          // ...  any other cases;

                case WM_COMMAND:

                      switch(LOWORD(wParam)) {

                      case ID_EXIT:
                           PostQuitMessage(0);

                           return 0;
                      }

                      return 0;

          // ...  any other cases and our default return;

          }

The code above checks if the "WM_COMMAND" message is received. If so, it checks to see if the item (LOWORD(wParam)) is our menu item. If it is, it exits. The reason why there's a switch/case setup with only one item is because you can use the same switch to check for any other menu items that this window has. For this example there's only one.

Dialog Boxes

Dialog boxes are very common to most Windows programs. They're usually there to accept input, but their uses are endless. The two main kinds of dialog box windows are modal and modeless. A modal dialog box has a parent window that you typically can't access or continue working with while the dialog is active. An example would be notepad. Run notepad and select "Open" from the file menu. When the file selection dialog window pops up, notice that you can't access the main notepad edit window until you close the dialog window. Modeless dialog boxes are different because you can leave them open and the application can continue operating at the same time allowing you to switch between the two if you'd like. Dialog boxes have many uses and variations including many possible controls to be placed on them (such as edit boxes, list boxes, combo boxes, progress meters, etc). I couldn't possibly sit here and explain how to work with every kind of control in a reasonable amount of time, so I'll just give a brief explanation of how to create a dialog box and how to use a simple control or two. The rest is up to you to figure out, but most controls work in similar ways.
The first step we'll take in creating a dialog window is actually designing the thing and placing controls. Take a look at the following screenshot of the previously mentioned resource editor:
Which translates to the following in the resource file:

     IDD_MAINDLG DIALOG DISCARDABLE  0, 0, 153, 164
     STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
     CAPTION "Simple Dialog Box"
     FONT 8, "MS Sans Serif"
     BEGIN
         DEFPUSHBUTTON   "OK",IDOK,65,114,50,14
         GROUPBOX        "",IDC_OUTLINE,3,5,146,81
         EDITTEXT        IDC_EDITBOX,9,19,133,12,ES_AUTOHSCROLL
         LTEXT           "Enter Some Text:",IDC_STATICENTER,9,9,89,9
     END

Again, you don't have to design your dialog boxes in a resource editor, but it makes life a whole lot happier if you're interested in how wisely you use your time.
Now that we have our dialog window designed, how do we actually make it go? A dialog window is just that -- a window. It has a window procedure and receives messages too. But in order to create one, we first must choose where to make it modal or modeless. Here are examples of both:

     // Create a Modal Dialog Box

       int success = DialogBox(hInstance,                    // instance handle;
                               MAKEINTRESOURCE(IDD_MAINDLG), // dialog identifier;
                               hWndParent,                   // parent/owner handle;
                               DlgProc);                     // window procedure;

     // If <i>success</i> is -1, the operation failed;

     // Create a Modeless Dialog Box

       HWND dlg = CreateDialog(hInstance,                    // instance handle;
                               MAKEINTRESOURCE(IDD_MAINDLG), // dialog identifier;
                               hWndParent,                   // parent/owner handle;
                               DlgProc);                     // window procedure;

       if(dlg==NULL) {

              return 0;
       } else ShowWindow(dlg,SW_SHOW);

As you can see, creating either type is not too difficult. Because I didn't mention before, I will now: MAKEINTRESOURCE is simply a macro that converts an integer into a compatible resource value. There are also other ways to create dialog boxes, but the above methods work out well. An important note to make when working with modeless dialog boxes is a function called IsDialogMessage, whose parameters are the dialog window handle and the current message from within the message loop. The function checks whether the message is for the dialog box in question and processes it if indeed it is. If the function succeeds (non-zero return), then you don't need to translate or dispatch the message because it has been taken care of already by IsDialogMessage.
Now we'll take a look at the dialog procedure. As you can see, its not too different from our previous window procedure:

     BOOL CALLBACK DlgProc(HWND dlg, UINT msg, WPARAM wParam, LPARAM lParam) {

          switch(msg) {

                case WM_INITDIALOG:
                     return TRUE;

                case WM_COMMAND:

                      switch(LOWORD(wParam)) {

                         case IDOK:
                         {
                           char dlgStr[20];

                           if(GetDlgItemText(dlg, IDC_EDITBOX, (LPTSTR)dlgStr, 20))
                              MessageBox(dlg, dlgStr, "Text Message", MB_OK);

                           return TRUE;

                         } return TRUE;
                      }
                      return FALSE;

                case WM_CLOSE:

                     EndDialog(dlg, IDOK);
                     return TRUE;
    	}
          return FALSE;
     }

Again, the window procedure checks for "WM_COMMAND", this time checking to see if the button has been pressed. If it has, it calls GetDlgItemText to retrieve the text from the edit box called IDC_EDITBOX and repeats the text in a message box window. The functions used should be pretty self-explanatory. EndDialog is used to destroy our dialog box. You can change that to exit code or anything else you'd like. The "WM_INITDIALOG" message is received when the dialog box is initialized, before it is visible. This is the place where most people put initialization code or set up dialog box controls.

Closing

Well, that's about all you need to start hacking around with menus and dialog boxes. Dialog boxes are especially cool because of their ease of use and the ability to customize them to no end. If you are having trouble with something (Windows can be quite frustrating), drop me a line and I'll try to explain it better or offer some source code. Other than that, have fun and good luck. Until next time, I'm out.

Copyright 1999-2008 (C) FLIPCODE.COM and/or the original content author(s). All rights reserved.
Please read our Terms, Conditions, and Privacy information.