Menus
Menus are added to a program in two steps,
first you create the menu, using either a resource editor, or a normal text
editor, and then you add code to support it. The resource editor of your
compiler will actually maintain a text file for you, with a '.rc' extention.
If you don't want to use the resource editor of your compiler, you can edit
the .rc file directly, using notepad.
The Menu Resource
A sample .rc file might contain
the following for a menu definition:
//Microsoft Developer Studio generated
resource script.
//
#include "resource.h"
NOTE: I have eliminated portions of the
file to save space in this handout. If the compiler created a .rc file,
do not remove anything from it.
/////////////////////////////////////////////////////////////////////////////
//
// Menu
//
IDR_MENU1 MENU DISCARDABLE
BEGIN
POPUP "&File"
BEGIN
MENUITEM "E&xit", ID_MENUEXIT
END
END
In this example, IDR_MENU1 is the name of the menu, and MENU identifies
it as a menu resource. There are two menu items defined, "File"
and "Exit". The File menu item is a pop-up menu, which contains
only the Exit menu item. The ID_MENUEXIT is the ID for the menu item. This
is an integer value, which can be found in the resource.h file. For example,
the resource .h file should have an entry somewhere that looks like:
#define IDR_MENU1 101
#define ID_MENUEXIT 40001
The exact values (101, and 40001) are not important, but should be unique
in the resource.h file. If you use a resource editor, it will automatically
insure this, and will also make entries in both the .rc file, and the resource.h
file.
Now that the menu is defined (the entire menu has a unique ID, ID_MENU1,
and each menu item that performs a task has a unique ID, ID_MENUEXIT), you
must make the changes to your source code.
When your program is compiled, the .rc file is converted into a .res file,
and the .res file is eventualy linked with your .EXE file. Windows will
know how to find the resource in the .EXE file.
Source Code Changes
You must make two changes to your
program to use a menu. First, you must attach the menu (using the menu's
unique ID) to a window, and then you must write the code to have the program
respond to the menu selection event in the WndProc for your main window.
Attaching the menu
You attach a menu to a window class,
not a specific window. Any window created using that class, will be created
with that menu. Therefore, you must set the lpszMenuName member of
the WNDCLASS structure appropriately, before calling RegisterClass.
If you made the menu name a string (enclosed the names in double quotes)
in your .rc file, then you can simply use the same name for the lpszMenuName
value. If you used an integer ID for the menu (which most resource editors
do, and we did in our example above), you must use the MAKEINTRESOURCE macro
with that ID, for the value of lpszMenuName:
WndClass.lpszMenuName = "MyMenu"; // If your using "MyMenu"
in the .rc file
Responding to menu selections
WndClass.lpszMenuName = MAKEINTRESOURCE( IDR_MENU1 ); // If your using IDR_MENU1
Your program is sent a WM_COMMAND message
when a menu item is selected. Actually, your program will receive several
messages such as WM_SYSCOMMAND,WM_INITMENU, WM_MENUSELECT, and WM_INITMENPOPUP,
but Windows will handle all these messages for you. It's the WM_COMMAND
that is the end result of the user selecting a menu choice.
Message | WM_COMMAND |
wParam | The low-word portion contains the sending-control's
ID The high-word portion is a notification code (1 if message is from an accelerator, 0 if a menu) |
lParam | HWND of control generating message |
case WM_COMMAND:
switch( LOWORD(wParam) )
{
case ID_MENUEXIT:
// Do something
break;
case ID_ADDBUTTON:
// Do something
break;
}
break;
Parameter | Meaning |
hMenu | Handle to the menu |
uIDEnableItem | ID of the menuitem, or it's position (based on uCheck) |
uCheck | One of the following: MF_BYCOMMAND - Indicates that UIDCheckItem contains a menu item ID (default) MF_BYPOSITION - Indicates that uIDCheckItem contains a menu item position number MF_DISABLED - Indicates that the menu item should be disabled, but not grayed MF_ENABLED - Indicates that the menu item should be enabled MF_GRAYED - Indicates that the menu item should be grayed and disabled One of the MF_BYCOMMAND or MF_BYPOSITION flags can be combined with one of the MF_DISABLED, MF_ENABLED, or MF_GRAYED flags, using the | (OR) operator. |
Returns the previous enabled status, or 0xFFFFFFFF
upon error.
Parameter | Meaning |
hMenu | Handle to the menu |
uIDCheckItem | ID of the menuitem, or it's position (based on uCheck) |
uCheck | One of the following: MF_BYCOMMAND - Indicates that UIDCheckItem contains a menu item ID (default) MF_BYPOSITION - Indicates that uIDCheckItem contains a menu item position number MF_CHECKED - Menu state should be changed to checked MF_UNCHECKED - Menu state should be changed to unchecked One of the MF_BYCOMMAND or MF_BYPOSITION flags can be combined with one of the MF_CHECKED or MF_UNCHECKED flags, using the | (OR) operator. |
Returns the previous check status, or 0xFFFFFFFF
upon error.
Case WM_COMMAND:
if( wParam==ID_MENUTOGGLE ) //ID_MENUTOGGLE must be a valid menu ID
{
HMENU hMenu;
hMenu = GetMenu( hWnd );
CheckMenuItem( hMenu, ID_MENUTOGGLE, (WasChecked) ? MF_UNCHECKED : MF_CHECKED
);
WasChecked = !WasChecked; // WasChecked should be a global or static local
variable.
}
break;
Parameter | Meaning |
hMenu | Handle to the menu |
uItem | ID of the menuitem, or it's position (based on fByPosition) |
fByPosition | If TRUE then uItem contains the menu item position, if FALSE then it contains the menu item ID |
lpmii | Pointer to a MENUITEMINFO structure |
Borland C++ 5.0