Topic : Menus
Author : Unknown
Page : 1 Next >>
Go to page :


Menu


Menu is very important for all types of applications, it provides a primary way of letting user execute application commands. If we create applications using Application Wizard, mainframe menus will be implemented automatically for all SDI and MDI applications. For dialog-based applications, system menus will also be implemented, which can be used to execute system commands (Move the application window, resize it, minimize, maximize and close the application). Some user-friendly applications also include right-click pop up menus.

This chapter discusses in-depth topics on using and customizing menus, which include: how to customize the behavior of standard menus, how to make change to standard menu interface, how to implement owner-draw menu, how to create right-click menu, how to customize the system menu and implement message mapping for system menu items.

1 Message WM_COMMAND and UPDATE_COMMAND_UI

When creating an SDI application using Application Wizard, we will have a default menu added to the application. This menu has four sub-menus: File, Edit, View and Help, which contain the most commonly used commands for a typical application. At the beginning some of these commands are functional (such as View | Tool bar and File | Exit) but some are disabled (such as Edit | Copy). We have to add our own code in order to make them usable.

To activate a command, we need to add message handlers for it. For a general Windows( application, we need to pay attention to two messages: WM_COMMAND and UPDATE_COMMOND_UI.

Sample 1\Menu demonstrates how to handle two types of messages through simulating a cut, copy and paste procedure. Here, we make use of three default menu commands added by the Application Wizard: View | Cut, View | Copy, View | Paste. The application will enable View | Paste menu item only after View | Cut or View | Copy has been executed at least once (For demonstration purpose, command View | Copy and View | Cut do not actually copy data to the clipboard). Also, item View | Paste will be changed dynamically indicating if the newly copied data has been pasted.

The sample is started by generating standard SDI application using Application Wizard. The project is named "Menu" and four standard classes are CMenuApp, CMainFrame, CMenuDoc, and CMenuView respectively. All other settings are default. After compiling and executing the skeleton application, we will see a standard SDI application with a menu implemented in the mainframe window. By examining this menu, we can find that it has the following structure:

File

New...

Open...

Save...

Save As...

Separator

Recent File

Separator

Exit

Edit

Undo

Separator

Cut

Copy

Paste

View

Toolbar

Status Bar

Help

About...

By clicking "Edit" sub-menu, we will see that all the commands contained there are disabled. If we edit the menu resource and add additional commands, they will not become functional until we add message handlers for them.

Handling WM_COMMAND Command

The first step of enabling a menu command is to implement a WM_COMMAND message handler for it. This is exactly the same with what we did for a tool bar command in Chapter 1. Just as buttons contained in a tool bar, each menu command has a unique command ID. When the user clicks the menu item, the system detects the mouse events and sends a WM_COMMAND message to the application, with the command ID passed through WPARAM parameter. In MFC, as the application receives this message, a corresponding message handler will be called to execute the command. Again, the message mapping should be implemented by using ON_COMMAND macro.

The message mapping could be implemented either manually or through using Class Wizard. This procedure is also the same with adding message handlers for tool bar buttons as we did in Chapter 1. If we use Class Wizard, after invoking it, first we need to go to "Messages Maps" page. Then we need to select a class name from "Class name" combo box (In the sample, all the commands are handled in the document class, so we need to select "CMenuDoc" if it is not selected). Next, we need to find the command ID to which we want to add handlers in "Object IDs" window, and highlight "Command" item in "Messages" window. Now click "Add Function" button and confirm the member function name. After this, a new member function and the corresponding message mapping macros will be added to the application.

There is no difference between adding message handlers manually and adding them using Class Wizard. However, doing it manually will let us understand message mapping mechanism, which will make it easier for us to further customize the menu behavior.

In the sample application we will implement commands View | Cut, View | Copy and View |Paste. So at first WM_COMMAND type message handlers need to be added for commands ID_EDIT_COPY, ID_EDIT_CUT and ID_EDIT_PASTE in CMenuDoc class. The following shows the steps of adding them through using Class Wizard:

1) In file "MenuDoc.h", three member functions are declared in the class, they will be used to handle ID_EDIT_COPY, ID_EDIT_CUT and ID_EDIT_PASTE command execution:

class CMenuDoc : public CDocument

{

......

//{{AFX_MSG(CMenuDoc)

afx_msg void OnEditCopy();

afx_msg void OnEditCut();

afx_msg void OnEditPaste();

//}}AFX_MSG

......

}


2) In file "MenuDoc.cpp", message mapping macros are added to associate the member functions with the command IDs:

BEGIN_MESSAGE_MAP(CMenuDoc, CDocument)

//{{AFX_MSG_MAP(CMenuDoc)

ON_COMMAND(ID_EDIT_COPY, OnEditCopy)

ON_COMMAND(ID_EDIT_CUT, OnEditCut)

ON_COMMAND(ID_EDIT_PASTE, OnEditPaste)

//}}AFX_MSG_MAP

END_MESSAGE_MAP()

3) Three blank message handlers are added in file "MenuDoc.cpp":

void CMenuDoc::OnEditCopy()

{

}

void CMenuDoc::OnEditCut()

{

}

void CMenuDoc::OnEditPaste()

{

}


When first added, these functions are empty. We have to add our own code in order to support command execution.

By compiling and executing the sample application at this point, we will see that Edit | Copy, Edit | Cut and Edit | Paste menu items are all enabled. This is because three blank message handlers have just been added.

Enabling & Disabling a Command

The sample application will not actually cut, copy or paste data. The three commands will be implemented just to simulate data copy and paste procedure. Before going on to implement it, we need to make following assumptions.

Suppose the application supports only internal data copy, cut and paste (it does not accept data from other applications through using system clipboard). Before command Edit | Copy or Edit | Cut is executed, there should be no data stored in the "local clipboard". Therefore, if we execute Edit | Paste command at this time, there will be an error. To avoid this, we need to disable Edit | Paste command before data has been copied to the clipboard.

The state of menu item can be set thought handling UPDATE_COMMAND_UI message. The parameter comes along with this message is a pointer to CCmdUI type object, which can be used to enable or disable a command, set or remove check for a menu item. Handling this message for menu items is the same with that of tool bar controls.

So it is easy to find out a mechanism for updating command Edit | Paste: we need to declare a Boolean type variable in class CMenuDoc and initialize it to FALSE. We can set this flag to TRUE when Edit | Cut or Edit | Copy command is executed, and enable Edit | Paste command only if this flag is set.

In the sample application, this Boolean variable is CMenuDoc::m_bPasteAvailable. The following code fragment shows how it is declared and initialized in the constructor of class CMenuDoc:

class CMenuDoc : public CDocument

{

......

protected:

BOOL m_bPasteAvailable;

......

}

CMenuDoc::CMenuDoc()

{

m_bPasteAvailable=FALSE;

}


The value of CmenuDoc::m_bPasteAvailable is set to TRUE when user executes either Edit | Copy or Edit | Cut command:

void CMenuDoc::OnEditCopy()

{

m_bPasteAvailable=TRUE;

}

void CMenuDoc::OnEditCut()

{

m_bPasteAvailable=TRUE;

}


Now we will use CMenuDoc::m_bPasteAvailable to enable Edit | Paste menu item when it becomes TRUE. In MFC, menu items are updated through handling UPDATE_COMMAND_UI messages. When the state of a menu command needs to be updated, UPDATE_COMMAND_UI message will be automatically sent to the application. If there exists a corresponding message handler, it will be called for updating the corresponding menu item. Otherwise, the menu item will remain unchanged.

Adding an UPDATE_COMMAND_UI message handler is the same with adding a WM_COMMAND message handler. After invoking the Class Wizard, we need to select the class name, the command ID, and highlight "UPDATE_COMMAND_UI" instead of "WM_COMMAND" in "Messages" window. Finally, we need to click button "Add Function".

After adding the message handler for ID_EDIT_PASTE command, we will have a new member function declared in class CMenuDoc, and a new message mapping macro added to the class implementation file. In addition, we will have an empty function that could be modified to implement message handling:

void CMenuDoc::OnUpdateEditPaste(CCmdUI *pCmdUI)
{

}


The only parameter to this function is a pointer to CCmdUI type object. Here, class CCmdUI has several member functions, which can be used to set the state of the menu item. To enable or disable the menu item, we can call function CCmdUI::Enable(...). The function has only one Boolean type parameter, we can pass TRUE to enable the menu item and pass FALSE to disable it. Because the state of Edit | Paste command depends upon variable CMenuDoc::m_bPasteAvailable, we can use it to set the state of menu item:

void CMenuDoc::OnUpdateEditPaste(CCmdUI* pCmdUI)
{

pCmdUI->Enable(m_bPasteAvailable);

}


By compiling and executing the sample application at this point, we will see that Edit | Paste command is disabled at the beginning, and after we execute either Edit | Cut or Edit | Copy command, it will be enabled.

Changing Menu Text

We will go

Page : 1 Next >>