Teach Yourself Visual C++® 5 in 24 Hours

Previous chapterNext chapterContents


- Hour 10 -
Menus

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.

What Is a 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.

Command Message Routing

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:

1. The CMainFrame object

2. The main MDI frame window

3. The active child frame of an MDI frame window

4. The view that is associated with the MDI child frame

5. The document object associated with the active view

6. The document template associated with the document object

7. The CWinApp object


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.

MFC Support for Menus

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.

Adding New Menu Items

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.

Opening the Menu Resource

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.

Editing the Menu Resource

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:

1. Double-click the empty box on the File menu to display the Menu Properties dialog box.

2. To add a menu item, provide a menu ID and caption for the new menu item. By convention, menu IDs begin with ID_ and then you include the name of the top-level menu. For this example, enter ID_FILE_HELLO as the menu ID and &Hello as the menu caption.

3. Optionally, you can provide a prompt that is displayed in the status bar when the new menu item is highlighted.

4. Click anywhere outside the Properties dialog box to return to the editor.

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.

Menu Item Properties

Several optional properties can be applied to a menu item via the Properties dialog box:

Adding a Message-Handling Function

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:

1. Open ClassWizard by pressing Ctrl+W or by right-clicking in a source code window and selecting ClassWizard from the menu.

2. Select the Message Maps tab and select the class that will handle the message from the Class Name combo box--in this case, CMainFrame.

3. Select the object that is generating the message from the Object ID list box--in this case, ID_FILE_HELLO. Two message-handling functions are displayed in the Messages list box.

4. Select the COMMAND message from the Messages list box and click the Add Function button. Accept the default name suggested by ClassWizard for the function name--OnFileHello.

5. Click OK to close ClassWizard.

Edit the CMainFrame::OnFileHello function so that it looks like the function provided in Listing 10.1.

TYPE: Listing 10.1. The CMainFrame::OnFileHello message-handling function.

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.

Creating a Shortcut Menu

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.

Creating the Resource

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.

Table 10.1. Menu items added to the ID_POPUP menu resource.

Menu ID Caption
ID_LIONS &Lions
ID_TIGERS &Tigers
ID_BEARS &Bears

Adding Message-Handling Functions

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.

TYPE: Listing 10.2. New member variables for the CMenuView class.

// 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.

TYPE: Listing 10.3. The constructor for CMenuView.

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.

TYPE: Listing 10.4. The CMenuView::OnDraw member function.

void CMenuView::OnDraw(CDC* pDC)
{
    pDC->TextOut( m_ptMsg.x, m_ptMsg.y, m_szMsg );

}

Trapping Messages

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.

1. Open ClassWizard by pressing Ctrl+W or right-clicking in a source code window and selecting ClassWizard from the menu.

2. Select the Message Maps tab and select the class that will handle the message from the Class Name combo box--in this case, CMenuView.

3. Select the object that is generating the message from the Object ID list box--in this case, use one of the values from Table 10.2.

4. Select a message from the Messages list box and click the Add Function button. Accept the default name suggested by ClassWizard for the function name.

5. Repeat this process for all entries in Table 10.2.

6. Click OK to close ClassWizard.

Table 10.2. Values used to create message-handling functions.

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.

TYPE: Listing 10.5. Popping up a menu when a right mouse button is clicked.

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.

TYPE: Listing 10.6. Message-handling functions for floating menu items.

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.

Using Keyboard Accelerators

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.

Displaying Keyboard Accelerator Resources

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.

Adding Keyboard Accelerators

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.

A Keyboard Accelerator Example

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.

Table 10.3. The new accelerator resource for the Menu project.

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.

Summary

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.

Q&A

Q I added a new item to my menu, but it's gray. I've checked the menu attributes to make sure that the menu should be enabled; why is the menu item still gray?

A Make sure that you have provided a message-handling function for the menu item. The MFC framework will not enable a menu item that doesn't have a message handler.

Q All the menu items with keyboard accelerators that are provided by MFC and AppWizard place the accelerator label to the far right of the menu window. How can I provide that effect for my controls?

A Use the \t tab escape sequence between your menu item and the accelerator text. For example, the caption for the Bears menu item would be
&Bears\tCtrl+B

Workshop

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."

Quiz

1. What MFC class is used to manage menus?

2. What message is handled when providing a shortcut menu?

3. What visual cue should be provided for a menu that leads to a dialog box that requires further input from the user?

4. What is a mnemonic?

5. What is a keyboard accelerator?

Exercises

1. The File|Enable Hello and File|Check Hello menu items are not updated to reflect the current state of the application. Add update command UI handlers for these menu items so that their captions read File|Disable Hello and File|Uncheck Hello when appropriate.

2. Add accelerators for the Lions and Tigers shortcut menu items in the Menu project.


Previous chapterNext chapterContents


Macmillan Computer Publishing USA

© Copyright, Macmillan Computer Publishing. All rights reserved.