Menus are an essential part of most Windows programs. With the exception of some simple dialog box-based applications, all Windows programs offer some type of menu.
In this hour, you will learn
In this hour you will also modify a menu created by AppWizard and create a floating context menu.
A menu is a list of command messages that can be selected and sent to a window.
To the user, a menu item is a string that indicates a task that can be performed by the application. Each menu item also has an ID that is used to identify the item when routing window messages. This ID is also used when modifying attributes for the menu item.
Menus are usually attached to a window, although many applications support floating pop-up menus that can appear anywhere on the desktop. Later in this hour, you create a floating pop-up menu that is displayed when the right mouse button is pressed. These menus are often used to provide context-sensitive help and offer different menu choices depending on the window that creates the menu.
In order to make Windows programs easier to use, most programs follow a common set of guidelines regarding the appearance of their menus. For example, menu items leading to dialog boxes that require additional user input are usually marked with an ellipsis (...).
Another user interface requirement is a mnemonic, or underlined letter for each menu item. When this letter is pressed, the appropriate menu item is selected. This letter is usually the first letter of the menu item; however, in some cases another letter is used. For example, the Exit menu item found in the File menu uses X as its mnemonic. You must be careful not to duplicate letters used for mnemonics; if you do, the menu won't work properly.
Menus are sometimes nested, which means that one menu item is actually a submenu with a series of additional menu items. A menu item that leads to a nested submenu has a right arrow to indicate that more selections are available. You can see an example of a nested menu structure in the menus used by the Windows 95 Start button, as shown in Figure 10.1.
Figure 10.1.
The nested menu structure used by the Start button.
Before you learn about creating and modifying menus, look at how menu messages are handled by Windows programs in general and MFC programs in particular.
A menu is always associated with a particular window. In most MFC programs, it is associated with the main frame window, which also contains the application's toolbar and status bar. When a menu item is selected, a WM_COMMAND message is sent to the main frame window; this message includes the ID of the menu item. The MFC framework and your application convert this message into a function call, as described in Hour 8, "Messages and Event-Driven Programming."
In an MFC application, many windows can receive a menu selection message. In general, any window that is derived from the CCmdTarget class is plugged into the MFC framework's message loop. When a menu item is selected, the message is offered to all the command target objects in your application, in the following order:
Just a Minute: This list might seem like a large number of steps to take, but it's actually not very complicated in practice. Usually, a menu item is handled by one type of object: a view or main frame. Menu messages are rarely handled directly by the document template or child frame objects.
Figure 10.2 shows a simplified map of how commands are routed in an MFC application.
In most cases, you can use ClassWizard to configure the message maps required to route menu selection messages to their proper destinations.
You can create menus dynamically or as static resources that are added to your program. The MFC class library provides a CMenu class that simplifies handling menus and is used for the examples in this hour.
Figure 10.2.
Menu command routing in an MFC application.
AppWizard generates a menu resource for programs that it creates. This menu resource can be edited to add extra menu items for your application, or you can create new menu resources for your application.
For the examples used in this hour, create a sample SDI application called Menu. This program is used to demonstrate how menu resources are created and modified.
One of the easiest tasks to perform with a menu is adding a new menu item. In order to use a new menu item, you must do two things:
These steps are explained in the next two sections.
To display the current menu resources, select the ResourceView tab in the project workspace window. Expand the resource tree to show the different resource types defined for the current project; one of the folders is labeled Menu.
Open the Menu folder to display the resources defined for the project's menus. Every multiple-document application created by AppWizard has two menu resources. MDI applications use an IDR_MAINFRAME menu when no views are active. They also have an additional menu item used when a view is active. The name of this menu resource is based on the application name, such as IDR_xxxTYPE, where xxx is replaced by the program's name. For example, IDR_FOOTYPE is the second menu resource created for a program named Foo.
SDI applications, such as the Menu example, have a single menu created by AppWizard named IDR_MAINFRAME. This is the menu displayed by default for single-document applications. Every AppWizard program begins with the same menu; supplying any modifications that are required for your application is up to you.
Open the menu resource by double-clicking the menu resource icon. The menu is displayed in the resource editor ready for editing. When the menu is initially loaded into the editor, only the top-level menu bar is displayed. Clicking any top-level menu item displays the pop-up menu associated with that item, as shown in Figure 10.3.
Figure 10.3.
Using the Developer Studio resource editor to edit a menu resource.
The last item of every menu is an empty box. This box is used to add new menu items to the menu resource. All menu items are initially added to the end of a menu resource and then moved to their proper position. To add a new menu item, follow these steps:
After you've added the new menu item you can move it to a new position by dragging it with the mouse. Changing the menu position does not change any of its attributes.
Several optional properties can be applied to a menu item via the Properties dialog box:
After adding a menu item to the application's menu, the next step is to add a message-handling function to handle the new menu item. As discussed in Hour 8, ClassWizard is used to create message-handling functions for MFC-based Windows programs. To add a message-handling function for the ID_FILE_HELLO menu item, follow these steps:
Edit the CMainFrame::OnFileHello function so that it looks like the function provided in Listing 10.1.
void CMainFrame::OnFileHello() { AfxMessageBox( "Hello from the File menu" ); }
Just a Minute: These basic steps are used to add all the menu items used in examples for the remaining hours in this book. The Developer Studio tools are so easy to use that adding a new menu item will be second nature in no time.
New Term: A shortcut menu, sometimes called a pop-up or context menu, is displayed by right-clicking on a window. Shortcut menus provide a list of commonly used actions.
Creating a shortcut menu is similar to modifying an existing menu except that a new menu resource must be created as the first step. Most shortcut menus are displayed in response to the WM_CONTEXTMENU message, which is sent when the right mouse button is pressed.
Use the Developer Studio resource editor to create the context menu. To create the new menu resource, use one of the following techniques:
Both of these methods opens a new menu resource for editing. Add a dummy caption for the first top-level item on the menu bar. This caption is not displayed by the menu; it is used only as a placeholder.
Open the Properties dialog box for the menu resource by double-clicking the edge of the menu resource, and change the resource ID to ID_POPUP. Using the values from Table 10.1, add three menu items under the dummy label.
Menu ID | Caption |
ID_LIONS | &Lions |
ID_TIGERS | &Tigers |
ID_BEARS | &Bears |
The new context menu is displayed when a right mouse click is detected on the application's view. After a menu item has been selected, a message is displayed at the menu's location, similar to the message displayed in the MouseTst example from Hour 8.
You must add two new variables to the CMenuView class: a CString variable that stores the message and a CPoint variable that stores the location of the pop-up menu. Add the source code provided in Listing 10.2 to the CMenuView class after the //Implementation comment.
// Implementation protected: CPoint m_ptMsg;
CString m_szMsg; The constructor for CMenuView must initialize the m_ptMsg variable. Edit the constructor for CMenuView, found in MenuView.cpp, so it looks like the source code in Listing 10.3.
CMenuView::CMenuView() { m_ptMsg = CPoint(0,0);
} The CMenuView::OnDraw member function resembles the OnDraw member function from CMouseTestView in Hour 8. Both functions use the TextOut function to display a message at a certain point in the view. Edit the CMenuView::OnDraw function so that it looks like the function provided in Listing 10.4. You must remove a few lines of AppWizard-supplied code.
void CMenuView::OnDraw(CDC* pDC) { pDC->TextOut( m_ptMsg.x, m_ptMsg.y, m_szMsg );
}
Use ClassWizard to add four new message-handling functions to the CMenuView class: three message-handling functions for the new menu items and one message-handling function to detect the right-click from the mouse button. The steps used to add the message-handling functions are similar to the ones used earlier when modifying an existing menu, except these messages are handled by the CMenuView class.
Object ID | Message | Function |
CMenuView | WM_CONTEXTMENU | OnContextMenu |
ID_LIONS | COMMAND | OnLions |
ID_TIGERS | COMMAND | OnTigers |
ID_BEARS | COMMAND | OnBears |
The source code for the CMenuView::OnContextMenu message-handling function is provided in Listing 10.5.
void CMenuView::OnContextMenu(UINT nFlags, CPoint point) { CMenu zooMenu; // Store popup point, and convert to client coordinates // for the drawing functions. m_ptMsg = point; ScreenToClient( &m_ptMsg ); zooMenu.LoadMenu( ID_POPUP ); CMenu* pPopup = zooMenu.GetSubMenu( 0 ); pPopup->TrackPopupMenu( TPM_LEFTALIGN|TPM_RIGHTBUTTON, point.x, point.y, this );
} When a right mouse click is detected, the WM_CONTEXTMENU message is sent to the application and the MFC framework calls the OnContextMenu message handler. The OnContextMenu function creates a CMenu object and loads the ID_POPUP menu resource. The floating menu is displayed by calling GetSubMenu and TrackPopupMenu.
The GetSubMenu function is used to skip past the dummy menu item at the top of the ID_POPUP menu resource. The GetSubMenu function returns a temporary pointer to the pop-up menu. Calling TrackPopupMenu causes the pop-up menu to be displayed and the menu item selection to automatically follow the mouse cursor.
The source code for handling menu selection messages sent to the CMenuView class is provided in Listing 10.6.
void CMenuView::OnLions() { m_szMsg = "Lions are out"; InvalidateRect( NULL ); } void CMenuView::OnTigers() { m_szMsg = "Tigers are afoot"; InvalidateRect( NULL ); } void CMenuView::OnBears() { m_szMsg = "Bears are hungry"; InvalidateRect( NULL );
} Each of the message-handling functions in Listing 10.6 works in a similar way: A message is stored in the m_szMsg member variable, and the view rectangle is invalidated. This causes a WM_PAINT message to be sent to the MFC framework, which in turn calls the OnDraw function to display the message.
Build the Menu project. Experiment by right-clicking in the main view window. Selecting any menu item from the shortcut menu will cause a message to be displayed, as shown in Figure 10.4.
Figure 10.4.
Selecting an item from the context menu.
New Term: Keyboard accelerators are keyboard shortcuts to message-handling functions.
A keyboard accelerator provides easy access to commonly accessed program functions. Each keyboard accelerator is a sequence of keystrokes that are translated into a Windows WM_COMMAND message, just as if a menu item were selected. This message is routed to a specific command handler.
AppWizard creates several keyboard accelerators automatically for your SDI and MDI applications. The following are some of the more common accelerators:
Just a Minute: There is no requirement that a keyboard accelerator must be mapped to a menu item. However, finding an action that is useful as a keyboard accelerator but not useful as a menu item is extremely rare.
Keyboard accelerators are resources and are displayed and edited much like menu resources. To see the keyboard accelerators for the Menu sample project, open the IDR_MAINFRAME Accelerator resource folder in the project workspace. The keyboard accelerators used by the project will be displayed as shown in Figure 10.5.
Figure 10.5.
Displaying the keyboard accelerators associated with the Menu sample project.
Just a Minute: An MDI program will have at least two accelerator resources. Only one resource identifier is in use at a time; the current accelerator resource has the same identifier as the current menu resource.
To create a new keyboard accelerator, bring up the Accel Properties dialog box by double-clicking the empty line at the bottom of the accelerator list. The Accel Properties dialog box is shown in Figure 10.6.
Figure 10.6.
Use the Accel Properties dialog box to add new accelerator resources to your project.
Time Saver: You can also bring up the Accel Properties dialog box by pressing the Insert key on your keyboard.
Each keyboard accelerator has several properties:
The simplest way to fill in the Accel Properties dialog box is to click the button labeled Next Key Typed. After clicking this button, the dialog box will use the next keystroke combination to fill in the properties for the accelerator.
CAUTION: Avoid using the ASCII value type for your keyboard accelerators because they behave unpredictably in the presence of Shift and Caps Lock keys. The virtual keycode is much more reliable and is not affected by your keyboard's Shift and Caps Lock states.
To illustrate how accelerators are added to Windows applications, you can add a keyboard accelerator to the Menu sample project. The accelerator will perform the same action as selecting Bears from the pop-up shortcut menu.
Open the IDR_MAINFRAME accelerator resource folder, and add a new accelerator resource to the Menu project using the values from Table 10.3.
ID | Key | Modifiers | Type |
ID_BEARS | B | Ctrl | Virtual |
Build the Menu project. Instead of selecting an item from the shortcut menu, try pressing Ctrl+B on your keyboard; you will get the same message as when you select Bears from the shortcut menu.
In this hour, you learned about the use of menus in Windows applications. You learned about the routing of menu command messages, as well as methods for modifying and creating menu resources. As an example, you created an application that displays a floating pop-up menu when the right mouse button is clicked.
&Bears\tCtrl+B
The Workshop is designed to help you anticipate possible questions, review what you've learned, and begin thinking ahead to putting your knowledge into practice. The answers to the quiz are found in Appendix B, "Quiz Answers."
© Copyright, Macmillan Computer Publishing. All rights reserved.