
Beginning Visual C++ 2005 (2006) [eng]-1
.pdf
Working with Menus and Toolbars
always included the DECLARE_MESSAGE_MAP() macro. All of the view (and other) classes derived from CWnd also have it.
CCmdTarget
CWinThread |
CWnd |
CDocument |
CWinApp |
CFrameWnd |
CView |
CMDIChildWnd |
CMDIFrameWnd |
CCtrlView |
CEditView
Figure 13-1
If you are adding your own members to a class directly, it’s best to leave the DECLARE_MESSAGE_MAP() macro as the last line in the class definition. If you do add members after DECLARE_MESSAGE_MAP(), you’ll also need to include an access specifier for them: public, protected, or private.
Message Handler Definitions
If a class definition includes the macro DECLARE_MESSAGE_MAP(), the class implementation must include the macros BEGIN_MESSAGE_MAP() and END_MESSAGE_MAP(). If you look in Sketcher.cpp, you’ll see the following code as part of the implementation of CSketcherApp:
BEGIN_MESSAGE_MAP(CSketcherApp, CWinApp) ON_COMMAND(ID_APP_ABOUT, &CSketcherApp::OnAppAbout) // Standard file based document commands ON_COMMAND(ID_FILE_NEW, &CWinApp::OnFileNew)
ON_COMMAND(ID_FILE_OPEN, &CWinApp::OnFileOpen) // Standard print setup command
ON_COMMAND(ID_FILE_PRINT_SETUP, &CWinApp::OnFilePrintSetup) END_MESSAGE_MAP()
This is a message map. The BEGIN_MESSAGE_MAP() and END_MESSAGE_MAP() macros define the boundaries of the message map, and each of the message handlers in the class appears between these macros. In
679

Chapter 13
the preceding case, the code is only handling one category of message, the type of WM_COMMAND message called a command message, which is generated when the user selects a menu option or enters an accelerator key. (If that seems clumsy, it’s because there’s another kind of WM_COMMAND message called a control notifications message, as you’ll see later in this chapter.)
The message map knows which menu or key is pressed by the identifier (ID) that’s included in the message. There are four ON_COMMAND macros in the preceding code, one for each of the command messages to be handled. The first argument to this macro is an ID that is associated with one particular command, and the ON_COMMAND macro ties the function name to the command specified by the ID. Thus, when a message corresponding to the identifier ID_APP_ABOUT is received, the function OnAppAbout() is called. Similarly, for a message corresponding to the ID_FILE_NEW identifier, the function OnFileNew() is called. This handler is actually defined in the base class, CWinApp, because are the two remaining handlers.
The BEGIN_MESSAGE_MAP() macro has two arguments. The first argument identifies the current class name for which the message map is defined and the second provides a connection to the base class for finding a message handler. If a handler isn’t found in the class defining the message map, the message map for the base class is then searched.
Note that command IDs such as ID_APP_ABOUT are standard IDs defined in MFC. These correspond to messages from standard menu items and toolbar buttons. The ID_ prefix is used to identify a command associated with a menu item or a toolbar button, as you’ll see when I discuss resources later. For example, ID_FILE_NEW is the ID that corresponds to the File > New menu item being selected, and ID_APP_ ABOUT corresponds to the Help > About menu option.
There are more symbols besides WM_COMMAND that Windows uses to identify standard messages. Each of them is prefixed with WM_ for Windows Message. These symbols are defined in Winuser.h, which is included in Windows.h. If you want to look at them, you’ll find Winuser.h in the include sub-folder to the VC folder containing your Visual C++ 2005 system.
There’s a nice shortcut for viewing a .h file. If the name of the file appears in the Editor window, you can just right-click it, and select the menu item Open Document “Filename.h” from the pop-up menu.
Windows messages often have additional data values that are used to refine the identification of a particular message specified by a given ID. The WM_COMMAND message, for instance, is sent for a whole range of commands, including those originating from selecting a menu item or a toolbar button.
Note that when you are adding message handlers manually you should not map a message (or in the case of command messages, a command ID) to more than one message handler in a class. If you do, it won’t break anything, but the second message handler is never called. Normally you add message handlers through the properties window and, in this case, you will not be able to map a message to more than one message handler. If you want to see the properties window for a class, right-click a class name in Class View and select Properties from the pop-up menu. You add a message handler by selecting the Messages button at the top of the Properties window that is displayed (Figure 13-2). You can figure out which button is the Messages button by hovering the mouse cursor over each button until the tooltip displays.
Clicking the Messages button brings up a list of message ID; however, before I go into what you do next, I need to explain a little more about the types of messages you may be handling.
680

Working with Menus and Toolbars
Figure 13-2
Message Categories
There are three categories of messages that your program may be dealing with, and the category to which it belongs determines how a message is handled. The message categories are:
Message Category |
Description |
|
|
Windows |
These are standard Windows messages that begin with the WM_ prefix, |
messages |
with the exception of WM_COMMAND messages that we shall come to in a |
|
moment. Examples of Windows messages are WM_PAINT, which indicates |
|
that you need to redraw the client area of a window, and WM_LBUTTONUP, |
|
which signals that the left mouse button has been released. |
Control notification |
These are WM_COMMAND messages sent from controls (such as a list box) to |
messages |
the window that created the control or from a child window to a parent |
|
window. Parameters associated with a WM_COMMAND message enable |
|
messages from the controls in your application to be differentiated. |
|
Table continued on following page |
681

Chapter 13
Message Category |
Description |
|
|
Command |
These are also WM_COMMAND messages that originate from the user |
messages |
interface elements, such as menu items and toolbar buttons. MFC defines |
|
unique identifiers for standard menu and toolbar command messages. |
|
|
The standard Windows messages in the first category are identified by the WM_-prefixed IDs that Windows defines. You’ll be writing handlers for some of these messages in the next chapter. The messages in the second category are a particular group of WM_COMMAND messages that you’ll meet in Chapter 16 when you work with dialog boxes. We’ll deal with the last category, messages originating from menus and toolbars, in this chapter. In addition to the message IDs defined by MFC for the standard menus and toolbars, you can define your own message IDs for the menus and toolbar buttons that you add to your program. If you don’t supply IDs for these items, MFC automatically generates IDs for you, based on the menu text.
Handling Messages in Your Program
You can’t put a handler for a message anywhere you like. The permitted sites for a handler depend on what kind of message is to be processed. The first two categories of message that you saw above, that is, standard Windows messages and control notification messages, are always handled by objects of classes derived from CWnd. Frame window classes and view classes, for example, are derived from CWnd, so they can have member functions to handle Windows messages and control notification messages. Application classes, document classes, and document template classes are not derived from CWnd, so they can’t handle these messages.
Using the properties window for a class to add a handler solves the headache of remembering where to place handlers, as it only offers you the IDs allowed for the class. For example, if you select CSketcherDoc as the class, you won’t be offered any of the WM_ messages in the properties window for the class.
For standard Windows messages, the CWnd class provides default message handling. Thus, if your derived class doesn’t include a handler for a standard Windows message, it is processed by the default handler defined in the base class. If you do provide a handler in your class, you’ll sometimes still need to call the base class handler as well, so that the message is processed properly. When you’re creating your own handler, a skeleton implementation of it is provided when you select the handler in the properties window for a class, and this includes a call to the base handler where necessary.
Handling command messages is much more flexible. You can put handlers for these in the application class, the document and document template classes, and of course in the window and view classes in your program. So, what happens when a command message is sent to your application, bearing in mind there are a lot of options as to where it is handled?
How Command Messages Are Processed
All command messages are sent to the main frame window for the application. The main frame window then tries to get the message handled by routing it in a specific sequence to the classes in your program. If one class can’t process the message, it passes it on to the next.
For an SDI program, the sequence in which classes are offered an opportunity to handle a command message is:
682

Working with Menus and Toolbars
1.
2.
3.
4.
5.
The view object
The document object
The document template object
The main frame window object
The application object
The view object is given the opportunity to handle a command message first and, if no handler has been defined, the next class object has a chance to process it. If none of the classes has a handler defined, default Windows processing takes care of it, essentially throwing the message away.
For an MDI program, things are only a little more complicated. Although you have the possibility of multiple documents, each with multiple views, only the active view and its associated document are involved in the routing of a command message. The sequence for routing a command message in an MDI program is:
1.
2.
3.
4.
5.
6.
The active view object
The document object associated with the active view
The document template object for the active document
The frame window object for the active view
The main frame window object
The application object
It’s possible to alter the sequence for routing messages, but this is so rarely necessary that I won’t go into it in this book.
Extending the Sketcher Program
You’re going to add code to the Sketcher program you created in the previous chapter to implement the functionality you need to create sketches. You’ll provide code for drawing lines, circles, rectangles, and curves with various colors and line thicknesses, and for adding annotations to a sketch. The data for a sketch is stored in a document, and you’ll also allow multiple views of the same document at different scales.
It will take several chapters to learn how to add everything that you need, but a good starting point would be to add menu items to deal with the types of elements that you want to be able to draw, and to select a color for drawing. You’ll make both the element type and color selection persistent in the program, which means that having selected a color and an element type, both of these remain in effect until you change one or the other of them.
The steps that you’ll work through to add menus to Sketcher are:
Define the menu items to appear on the main menu bar and in each of the menus.
Decide which of the classes in our application should handle the message for each menu item.
683

Chapter 13
Add message handling functions to the classes for the menu messages.
Add functions to the classes to update the appearance of the menus to show the current selection in effect.
Add a toolbar button complete with tooltips for each of the menu items.
Elements of a Menu
You’ll be looking at two aspects of dealing with menus with the MFC: the creation and modification of the menu as it appears in your application and the processing necessary when a particular menu item is selected — the definition of a message handler for it. Let’s look first at how you create new menu items.
Creating and Editing Menu Resources
Menus are defined external to the program code in a resource file and the specification of the menu is referred to as a resource. There are several other kinds of resources that you can include in your application; typical examples are dialogs, toolbars, and toolbar buttons. You’ll be seeing more on these as you extend the Sketcher application.
Having a menu defined in a resource allows the physical appearance of the menu to be changed without affecting the code that processes menu events. For example, you could change your menu items from English to French or Norwegian or whatever without having to modify or recompile the program code. The code to handle the message created when the user selects a menu item doesn’t need to be concerned with how the menu looks, only with the fact that it was selected. Of course, if you do add items to the menu, you’ll need to add some code for each of them to ensure that they actually do something!
The Sketcher program already has a menu, which means that it already has a resource file. We can access the resource file contents for the Sketcher program by selecting the Resource View pane, or if you have the Solution Explorer pane displayed, you can double-click Sketcher.rc. This switches you to the Resource View, which displays the resources. If you expand the menu resource by clicking on the + symbol, you’ll see that there are two menus defined, indicated by the identifiers IDR_MAINFRAME and IDR_SketcherTYPE. The first of these applies when there are no documents open in the application, and the second when you have one or more documents open. MFC uses the IDR_ prefix to identify a resource that defines a complete menu for a window.
You’re only going to be modifying the menu that has the identifier IDR_SketcherTYPE. You don’t need to look at IDR_MAINFRAME, as your new menu items will only be relevant when a document is open. You can invoke a resource editor for the menu by double-clicking its menu ID in Resource View. If you do this for IDR_SketcherTYPE, the Editor pane appears as shown in Figure 13-3.
Adding a Menu Item to the Menu Bar
To add a new menu item, you can just click the menu box with the text “Type Here” to select it and then type in your menu name. If you insert the ampersand (&) in front of a letter in the menu item, the letter is identified as a shortcut key to invoke the menu from the keyboard. Type the first menu item as E&lement. This selects l as the shortcut letter, so you will be able invoke the menu item by typing Alt+l. You can’t use E because it’s already used by Edit. When you finish typing the name, you can doubleclick the new menu item to display its properties as shown in Figure 13-4.
684

Working with Menus and Toolbars
Figure 13-3
Figure 13-4
685

Chapter 13
Properties are simply parameters that determine how the menu item will appear and behave. Figure 13-4 displays the properties for the menu item grouped by category. If you would rather have them displayed in alphabetical sequence, just click the second button from the left. Note that the Popup property is set as True by default; this is because the new menu item is at the top level on the menu bar so it would normally present a pop-up menu when it is selected. Clicking any property in the left column enables you to modify it in the right column. In this case you want to leave everything as it is so you can just close the Properties window. No ID is necessary for a pop-up menu item because selecting it just displays the menu beneath and there’s no event for your code to handle. Note that you get a new blank menu box for the first item in the pop-up menu, as well as one on the main menu bar.
It would be better if the Element menu appeared between the View and Window menus, so place the cursor on the Element menu item and keeping the left mouse button pressed, drag it to a position between the View and Window menu items, and release the left mouse button. After positioning the new Element menu item, the next step is to add items on the pop-up menu that corresponds to it.
Adding Items to the Element Menu
Select the first item (currently labeled “Type Here”) in the Element pop-up menu by clicking it; then type &Line as the caption and press the Enter key. You can see the properties for this menu item by doubleclicking it; the properties for this first item in the pop-up menu are shown in Figure 13-5.
Figure 13-5
686

Working with Menus and Toolbars
The properties modify the appearance of the menu item and also specify the ID of the message passed to your program when the menu item is selected. Here we have the ID already specified as ID_ELEMENT_ LINE, but you could change it to something else if you want. Sometimes it’s convenient to specify the ID yourself, such as when the generated ID is too long or its meaning is unclear. If you choose to define your own ID, you should use the MFC convention of prefixing it with ID_ to indicate that it’s a command ID for a menu item.
Because this item is part of a pop-up menu, the Popup property is False by default. You could make it another pop-up menu with a further list of items by setting the Popup property as True. As you see in Figure 13-5, you can display the possible values for the Popup property by selecting the down arrow.
Don’t you love the way pop-ups pop up all over the place?
You can enter a text string for the value of the Prompt property that appears in the status bar of your application when the menu item is highlighted. If you leave it blank, nothing is displayed in the status bar. I suggest you enter Line as the value for the Prompt property. Note how you get a brief indication of the purpose of the selected property at the bottom of the properties window. You want the default element selected in the application at start up to be a line, so you can set the Checked property value as True to get a check mark against the menu item to indicate when Line has been selected. Remember to add code to update check marks for the menu items when a different selection is made. The Break property can alter the appearance of the pop-up by shifting the item into a new column. You don’t need that here, so leave it as it is. Close the Properties window to save the values that you have set.
Modifying Existing Menu Items
If you think you may have made a mistake and want to change an existing menu item, or even if you just want to verify that you set the properties correctly, it’s very easy to go back to an item. Just double-click the item you’re interested in and the properties window for that item are displayed. You can then change the properties in any way that you want and close the window when you’re done. If the item you want to access is in a pop-up menu that isn’t displayed, just click the item on the menu bar to display the pop-up.
Completing the Menu
Now you can go through and create the remaining Element pop-up menu items that you need: &Rectangle, &Circle, and Cur&ve. Of course, all of these should have the Checked property left as False. You can’t use C as the hotkey for the last item, as hotkeys must be unique and you’ve already assigned C to the menu item for a circle. You can accept the default IDs ID_ELEMENT_RECTANGLE, ID_ELEMENT_CIRCLE, and ID_ELEMENT_CURVE for these. You could also set the values for the Prompt property value as Rectangle, Circle, and Curve respectively.
You also need a Color menu on the menu bar, with pop-up menu items for Black, Red, Green, and Blue. You can create these, starting at the empty menu entry on the menu bar, using the same procedure that you just went through. Set Black as checked so that is the default color. You can use the default IDs (ID_COLOR_BLACK, etc.) as the IDs for the menu items. You can also add the status bar prompt for each as the value of the Prompt property. After you’ve finished, if you drag Color so that it’s just to the right of Element, the menu should appear as shown in Figure 13-6.
Note that you need to take care not to use the same letter more than once as a shortcut in the pop-up, or in the main menu, for that matter. There’s no check made as you create new menu items, but if you rightclick with the cursor on the menu bar when you’ve finished edited it, you’ll get a pop-up that contains an item Check Mnemonics. Selecting this checks your menu for duplicate shortcut keys. It’s a good idea to do this every time you edit a menu because it’s easy to create duplicates by accident.
687

Chapter 13
Figure 13-6
That completes extending the menu for elements and colors. Don’t forget to save the file to make sure that the additions are safely stored away. Next, you need to decide in which classes you want to deal with messages from your menu items, and add member functions to handle each of the messages. For that you’ll be using the Event Handler wizard.
Adding Handlers for Menu Messages
To create an event handler for a menu item, right-click the item and select Add Event Handler from the pop-up that is displayed. If you try this with the Black menu item in the Color menu pop-up, you’ll see the dialog box shown in Figure 13-7.
As you can see, the wizard has already chosen a name for the handler function. You could change it but OnColorBlack seems like a good name to me.
You obviously need to specify the message type as one of the choices shown in the dialog box. The Message type: box in the window in Figure 13-7 shows the two kinds of message that can arise for a particular menu ID. Each type of message serves a distinct purpose in dealing with a menu item.
688