When deciding where to add the command handler, consider which class should logically perform the action that the command will carry out. For example, in the preceding code example, the Colors menu handler should be placed in the CMenus1View class because the View class has full access to all the functions of the document class.
Typically, in single document interface (SDI) single-pane applications, command handlers are located in the View class. In SDI multiple-pane applications, such as splitter windows, and multiple document interface (MDI) applications, command handlers are located in the CMainFrame class.
When designing the interface for a Windows-based application, remember that users must be able to see the results of menu actions and choices that they have made. You can write code that updates the look of the interface to reflect these actions. For example, you can add a check mark next to a menu item that the user has selected.
This section describes situations where the appearance of a menu requires updating, gives an overview of how you do this, and provides the necessary programming details.
This section includes the following topics:
Menu items are updated in response to the user's clicking the menu name. Menu items must be updated before they can be displayed.
When a user clicks a menu name, the Windows operating system sends a WM_INITMENU or WM_INITMENUPOPUP message to the framework, and the framework calls the update command handler.
When a menu item must be updated, the framework searches for and calls the update command handler associated with the menu item. The handler provides Windows with instructions about how each menu item should appear after a user action. Menu items must be updated before they are displayed.
The CCmdUI class provides the context for updating user-interface objects. For example, CCmdUI::Enable enables menu items so they can be selected, or disables them and displays them dimmed to indicate that they are unavailable.
By default, MFC displays all menu items as enabled if they have an associated command handler. Menu items with no handler are unavailable and appear dimmed.
For menu items that require visual updating, such as adding a check mark to indicate that the selected text is now bold, an update command handler changes the appearance of the menu item.
The following illustration shows the Message Map tab in ClassWizard. An Object ID is selected (ID_COLORS_BLACK) and the message selected is UPDATE_COMMAND_UI. The lower portion of the illustration shows the update handlers available. From here, you can add any update command handlers needed. To see an illustration of the Message Map tab in ClassWizard, click this icon.
Code in the Update Command Handler
The update handler receives a pointer to the command's user-interface object — in this case, a menu — as an argument. Use this argument to invoke the appropriate member function to update the user interface.
The following table summarizes the CCmdUI member functions as they pertain to menu items, dialog-box buttons, and controls.
Function Description
Enable Enables or disables the user interface item.
SetCheck Adds or clears a check mark.
SetRadio For a menu item, adds or clears a dot.
SetText Changes text displayed for a command user-interface item.
To add update commands for menu items
1. Start ClassWizard. In the Class name list box, find the appropriate class.
2. In the Object IDs list box, locate the ID of the item to which you want to add an update handler.
3. In the Messages list box, select Update Command UI.
4. Click Add Function, and accept the default name.
After you have added basic functionality to a menu, you can provide a keyboard-based approach to the same functionality. Accelerator keys are keystrokes that carry out a command without displaying menus and menu items.
Note The term "accelerator key" is used only in documentation for developers. In documentation for end users, an accelerator key is referred to as a "shortcut key."
For the developer, "shortcut key" refers to the underscored character on a menu or menu item. The end user reads about these underscored characters as "access keys."
An accelerator key produces the same command message and is processed by the same command handler as its corresponding menu item.
To implement accelerator keys for a menu item, you must complete two steps:
1. To add the accelerator combination to a menu, add the keystroke combination to the Caption string on the Menu Item Properties property sheet.
2. To connect the keystroke combination to the appropriate command ID, use the Accelerator editor to add the accelerator resource.
These steps are described in detail in the following procedures.
The following illustration shows the Menu Item Properties property sheet, which you use to add accelerators to menu items.
The following illustration shows the Accelerator Properties property sheet in the Accelerator editor.
u To add an accelerator key combination to the right of a menu item
1. In the Menu editor, double-click the menu item you want to edit. This displays the Menu Item Properties property sheet.
2. In the Caption box, edit the menu item's string. At the end of the existing caption, type \t followed by the string that represents the accelerator key combination.
The \t places a tab after the existing caption. For example, in a typical Copy command, the caption string appears as follows:
&Copy\tCtrl+C
To connect a keystroke combination to a command ID
1. In your project, click the ResourceView tab.
2. Click the Accelerator folder to open it, and double-click the accelerator table resource to open it.
3. On the Insert menu, click New Accelerator to display the Accelerator Properties property sheet.
4. In the Accelerator Properties property sheet, make the following changes:
a. In the ID box, select the corresponding command ID.
b. Select Next Key Typed.
c. Type the keystroke combination as if you were using the actual accelerator key.
5. Verify that the entry in the Key text box reflects the combination that you just typed, and then close the property sheet.
® Be sure that each accelerator combination is unique.
When creating accelerator keys, do not use reserved key combinations and keys used for backward compatibility with existing applications.
Windows interface guidelines reserve common key combinations for typical Windows-based commands, such as Copy (CTRL+C) and Print (CTRL+P), or for functionality within the Windows operating system.
For backward compatibility, some applications continue to support SHIFT+DELETE, CTRL+INSERT, and SHIFT+INSERT for the Cut, Copy, and Paste commands, respectively.
® Provide menu-driven methods to gain access to commands. Accelerator keys should never be the only method of access.
You can use shortcut menus to provide users with an optional way to access commonly used commands. Shortcut menus provide an efficient, object-centered method for interacting with an application.
Shortcut menus are generally displayed at the location of the mouse pointer. The content of a shortcut menu depends on what the user selects with the pointer. For example, if the user selects a string of text, a shortcut menu often displays common formatting or editing options. Users access shortcut menus by clicking the right mouse button.
Shortcut menu items should never be the only method available to access a command.
For more information about shortcut menus, see Windows Interface Guidelines for Software Design.This section includes the following topics:
Adding a Shortcut MenuThe methods for adding shortcut menus differ from the methods used to add top-level menus. You can create a shortcut menu in three ways:
® If you know all of the menu items that you want to appear in a shortcut menu, you can use the Resource editor to easily create shortcut menus.
® If you know only part of the menu content at design time, you can create the known part of the shortcut menu with the Resource editor, and then use AppendMenu to add other menu items later.
® If none of the contents of the pop-up is known until run time, you can use the Developer Studio Gallery and follow the steps below.
To add a shortcut menu resource using the Developer Studio Gallery
1. On the Developer Studio Project menu, click Add To Project, and then click Components and Controls.
2. Double-click the Developer Studio Components folder.
3. Select Pop-up Menu, click Insert, and then click OK.
4. Add the pop-up menu to an appropriate class in your application, such as the View class, and then click OK.
5. Modify the pop-up menu by removing the items that you do not want, and adding menu items as needed.
6. In the class where you put the shortcut menu, locate the OnContextMenu command handler.
7. Change the this variable to a call to ::AfxGetMainWnd.
8. If necessary, add code to determine whether the user has selected an item with the mouse pointer. For example, if the user clicks the right mouse button in an area for which a shortcut menu is not relevant, do not display a shortcut menu.
Some other possibilities for building shortcut menus are:
® If you need to build a menu dynamically, use a CMenu pointer Popup, and the functions CreatePopupMenu and AppendMenu.
® You also can create a menu that is a combination of a menu created with the Menu editor and a dynamic menu.
For more information about methods of creating a shortcut menu, search for AppendMenu in Visual C++ Help.
Adding a Shortcut Menu HandlerOnce you have the interface for your shortcut menus, you need to associate functionality with each item on the menu; this requires a message handler. When you use the Developer Studio Gallery to add a shortcut menu, it adds the following information to your application:
A declaration for an OnContextMenu member function in the declaration of your view class.
An ON_WM_CONTEXTMENU macro in the message map for your view class.
A skeletal definition for the OnContextMenu member function.
An overloaded PreTranslateMessage function, so that SHIFT+F10 invokes your shortcut menu, as it does in Microsoft Word and Microsoft Excel.
The following sample code shows how to write the handler OnContextMenu. OnContextMenu receives the location of the mouse event in screen coordinates, not in client coordinates like as do handlers such as OnLButtonDown. Before testing, the function converts the screen coordinates to client coordinates by using CWnd::ScreenToClient. To see the sample code, click this icon.
void CMenusDynamicView::OnContextMenu(CWnd*, CPoint point)
{
// First, we have to determine if the mouse is
// somewhere on the phrase. Begin by obtaining a
// rectangle that bounds the phrase.
CRect BoundingRectangle = GetPhraseBounds();
// Because the parameter is in screen coordinates,
// we'll have to convert it to client coordinates
// before doing any hit-testing.
CPoint pt(point);
ScreenToClient(&pt);
// Then, if the mouse is not the rectangle, exit this
// function.
if (FALSE == BoundingRectangle.PtInRect(pt))
return;
CMenu menu;
VERIFY(menu.LoadMenu(CG_IDR_POPUP_MENUS_DYNAMIC_VIEW));
CMenu* pPopup = menu.GetSubMenu(0);
ASSERT(pPopup != NULL);
// The four existing menu items (black, red, green
// and blue) were added to the popup menu using the
// menu editor. For demonstration purposes, 3 more
// menu items will be dynamically added to the popup.
// They'll be separated from the first 4.
pPopup->AppendMenu(MF_SEPARATOR);
CString prompt;
for (int i = 0; i < 3; i++)
{
prompt.LoadString(ID_COLORS_CYAN + i);
pPopup->AppendMenu(MF_STRING,
ID_COLORS_CYAN + i, prompt);
}
// Follow the chain of owners to find a window that's
// not a child. This window will serve as the owner
// of the popup menu.
CWnd* pWndPopupOwner = this;
while (pWndPopupOwner->GetStyle() & WS_CHILD)
pWndPopupOwner = pWndPopupOwner->GetParent();
// Finally, display the popup menu.
pPopup->TrackPopupMenu(
TPM_LEFTALIGN | TPM_RIGHTBUTTON,
point.x, point.y, pWndPopupOwner);
}
//Here's the function GetPhraseBounds:
CRect CMenusDynamicView::GetPhraseBounds()
{
CClientDC dc(this);
TEXTMETRIC tm;
dc.GetTextMetrics(&tm);
CMenusDynamicDoc* pDoc = GetDocument();
CString temp = pDoc->GetPhrase();
CSize cs = dc.GetTextExtent(temp, temp.GetLength());
// Recall that the phrase is centered on a point in
// the exact middle of the view. See View class's OnDraw.
// This makes creating a bounding rectangle a bit tricky.
// We'll have to divide the text extents by 2, and take into
// account the amount of descent of the current font.
CRect r;
GetClientRect(&r);
int x = (r.right / 2) - (cs.cx / 2);
int y = (r.bottom / 2) - (cs.cy / 2) - tm.tmDescent;
return CRect(CPoint(x, y), cs);
}
No comments:
Post a Comment