Now, you can do this for all four menu items, and you can ...



Advanced Menus

Menu items can be

• disabled (grayed out) or enabled

• checked or unchecked

You may want to disable a menu item that cannot be applied, rather than allow it to be chosen and then have to bring up an error message or do nothing. For example, you might want to disable an option that is not available at the moment.

An alternative would be to remove the unavailable item entirely from the menu. But you may prefer a grayed-out entry, which tells the user that the option is sometimes available. For example: email this document might be grayed out unless the user has a live internet connection.

You may want to use a check mark to show whether an item is currently selected. For example, in MathXpert there is a menu item

Large Numbers which brings up a popup showing two different ways large numbers can be displayed:

1,234,567

1234567

The one currently in use has a check mark. If the user chooses the unchecked item, the display of numbers changes and the next time this menu is brought up, the check mark is on the other item.

We will learn how to do these things in MFC.

Updating a Menu

Menu item updaters are functions that are called just before a menu item is displayed. The argument passed to an updater is a pointer to an object called a UI command. This object describes any changes that should be made to the appearance of the menu item such as enabled or disabled, checked or unchecked, modifications to the caption, etc.:

class CCmdUI

{

public:

// enable or disable menu item:

virtual void Enable(BOOL bOn = TRUE);

// check or uncheck menu item:

virtual void SetCheck(int nCheck = 1);

// change menu item caption:

virtual void SetText(LPCTSTR lpszText);

// etc.

};

Let's fix the Weather program so that the menu item Sunshine is disabled when the currently selected weather is already Sunshine. Use Event Handler Wizard. Remember, to do that you open your menu in the menu editor, right click the Sunshine item, then choose Add Event Handler:

[pic]

Make the selections shown. You have to select the class first and then select the message type. If you first select the message type and then the class, the message type goes back to the first one in the list, so be careful.

Then click Add and Edit. Edit the code to look like this:

void CWeatherView::OnUpdateWeatherSunshine(CCmdUI* pCmdUI)

{

CWeatherDoc* pDoc = GetDocument();

if(pDoc->GetWeather() == "Sunshine")

pCmdUI->Enable(FALSE); // disable it

else

pCmdUI->Enable(TRUE); // enable it

}

Do the same for all four menu items. You can check that it works.

Checking and unchecking menu items:

void CWeatherView::OnUpdateWeatherSnow(CCmdUI* pCmdUI)

{

CWeatherDoc* pDoc = GetDocument();

if(pDoc->GetWeather() == "Snow")

pCmdUI->SetCheck(1);

else

pCmdUI->SetCheck(0);

}

The Class CMenu

Menus are objects. They may have a visual appearance, but they also can exist in memory without being displayed. You can, for example, have two menus and switch which one is actually active at a given time. You can create menus dynamically, depending on the run-time situation. You can modify menus dynamically, inserting or deleting items, or changing the text.

Example: “Show grid” on the menu when the grid is not visible; “Hide grid” on the menu when the grid is visible. This is better than having both items there at all times, and better than having the irrelevant one grayed out.

Example: You can have a menu in French and a menu in English. You can switch them while the program is running, in response to a “Language | French” menu choice or a “Langue: Anglais” menu choice.

Example: When a mathematical expression in MathXpert is selected, the program constructs a menu of things you might be able to do to that expression. There are thousands of possibilities for this menu so it really must be constructed on the fly.

The MFC class CMenu is just a wrapper for data structures and functions that already exist in the Windows API: Please read about CMenu in the online help. The next slide shows what you find. If you are reading this on the Web, do not expect the links to be live--this is just cut and pasted. For live links, open Visual C++ online help.

Construction

|CMenu |Constructs a CMenu object. |

Initialization

|Attach |Attaches a Windows menu handle to a CMenu object. |

|Detach |Detaches a Windows menu handle from a CMenu object and returns the handle. |

|FromHandle |Returns a pointer to a CMenu object given a Windows menu handle. |

|GetSafeHmenu |Returns the m_hMenu wrapped by this CMenu object. |

|DeleteTempMap |Deletes any temporary CMenu objects created by the FromHandle member function. |

|CreateMenu |Creates an empty menu and attaches it to a CMenu object. |

|CreatePopupMenu |Creates an empty pop-up menu and attaches it to a CMenu object. |

|LoadMenu |Loads a menu resource from the executable file and attaches it to a CMenu object. |

|LoadMenuIndirect |Loads a menu from a menu template in memory and attaches it to a CMenu object. |

|DestroyMenu |Destroys the menu attached to a CMenu object and frees any memory that the menu occupied. |

Menu Operations

|DeleteMenu |Deletes a specified item from the menu. If the menu item has an associated pop-up menu, |

| |destroys the handle to the pop-up menu and frees the memory used by it. |

|TrackPopupMenu |Displays a floating pop-up menu at the specified location and tracks the selection of items on |

| |the pop-up menu. |

Menu Item Operations

|AppendMenu |Appends a new item to the end of this menu. |

|CheckMenuItem |Places a check mark next to or removes a check mark from a menu item in the pop-up menu. |

|CheckMenuRadioItem |Places a radio button next to a menu item and removes the radio button from all of the other |

| |menu items in the group. |

|SetDefaultItem |Sets the default menu item for the specified menu. |

|GetDefaultItem |Determines the default menu item on the specified menu. |

|EnableMenuItem |Enables, disables, or dims (grays) a menu item. |

|GetMenuItemCount |Determines the number of items in a pop-up or top-level menu. |

|GetMenuItemID |Obtains the menu-item identifier for a menu item located at the specified position. |

|GetMenuState |Returns the status of the specified menu item or the number of items in a pop-up menu. |

|GetMenuString |Retrieves the label of the specified menu item. |

|GetMenuItemInfo |Retrieves information about a menu item. |

|GetSubMenu |Retrieves a pointer to a pop-up menu. |

|InsertMenu |Inserts a new menu item at the specified position, moving other items down the menu. |

|ModifyMenu |Changes an existing menu item at the specified position. |

|RemoveMenu |Deletes a menu item with an associated pop-up menu from the specified menu. |

|SetMenuItemBitmaps |Associates the specified check-mark bitmaps with a menu item. |

|GetMenuContextHelpId |Retrieves the help context ID associated with the menu. |

|SetMenuContextHelpId |Sets the help context ID to be associated with the menu. |

Modifying a menu at run time

Let's fix the Weather menus so that the menu item for the current selection is just not on the menu, rather than being disabled or checked.

Thus when Sunshine is the current weather, the menu will list

Cloudy

Rainy

Snow

but not Sunshine. Then when we choose Snow, the menu should read

Cloudy

Rainy

Sunshine

That can't be done with a menu updater. We must manipulate the menu as an object.

The time to do this is when the document data is changed, in ChangeWeather.

static int menuID(CString s)

// return the menu id for Rain, Snow, etc.

{ if(s == "Sunshine")

return ID_WEATHER_SUNSHINE;

if(s == "Rain")

return ID_WEATHER_RAIN;

if(s == "Snow")

return ID_WEATHER_SNOW;

if(s == "Cloudy")

return ID_WEATHER_CLOUDY;

ASSERT(0); // all possibilities were listed

return 0;

}

void CWeatherDoc::ChangeWeather(CString newWeather)

{ CWnd* pMain = AfxGetMainWnd();

CMenu* pMenu = pMain->GetMenu();

CMenu* WeatherMenu = pMenu->GetSubMenu(4);

WeatherMenu->InsertMenu(-1, MF_BYPOSITION, menuID(m_Weather), m_Weather); //-1 says put it at the bottom

m_Weather = newWeather; // this line was there already

WeatherMenu->DeleteMenu(menuID(m_Weather), MF_BYCOMMAND);

}

To make this work properly, we also have to delete the Sunshine menu item from the initial menu in the menu editor. That does not, luckily, also delete the identifier ID_WEATHER_SUNSHINE. By working a bit harder we could get the new item added in the original order instead of at the bottom.

Try it at home...

................
................

In order to avoid copyright disputes, this page is only a partial summary.

Google Online Preview   Download