You cannot change the state of a menu item from its command user-interface handler if the menu is attached to a dialog box in Visual C++ (242577)
The information in this article applies to:
- The Microsoft Foundation Classes (MFC), when used with:
- Microsoft Visual C++, 32-bit Enterprise Edition 6.0
- Microsoft Visual C++, 32-bit Professional Edition 6.0
- Microsoft Visual C++, 32-bit Learning Edition 6.0
- Microsoft Visual C++ 2005 Express Edition
- Microsoft Visual C++ .NET (2003)
- Microsoft Visual C++ .NET (2002)
This article was previously published under Q242577 Note Microsoft Visual C++ .NET 2002 and Microsoft Visual C++ .NET 2003 support both the managed code model that is provided by the Microsoft .NET Framework and the unmanaged native Microsoft Windows code model. The information in this article applies only to unmanaged Visual C++ code. Microsoft Visual C++ 2005 supports both the managed code
model that is provided by the Microsoft .NET Framework and the unmanaged native
Microsoft Windows code model. SYMPTOMS Changing the state (enable/disable, check/uncheck, change
text) of a menu item from its command user-interface (UI) handler does not work
correctly if the menu is attached to a dialog box:
void CTestDlg::OnUpdateFileExit(CCmdUI* pCmdUI)
{
pCmdUI->Enable(FALSE); //Not calling the command handler, but does not show as disabled.
pCmdUI->SetCheck(TRUE); // Does not show check mark before the text.
pCmdUI->SetRadio(TRUE); // Does not show dot before the text.
pCmdUI->SetText("Close"); //Does not change the text.
}
CAUSE When a drop-down menu is displayed, the WM_INITMENUPOPUP
message is sent prior to displaying the menu items. The MFC CFrameWnd::OnInitMenuPopup function iterates through the menu items and calls the update
command UI handler for the item, if there is one. The appearance of each menu
item is updated to reflect its state (enabled/disabled,
checked/unchecked).
The update UI mechanism doesn't work for a dialog
box-based application because CDialog has no OnInitMenuPopup handler and it uses CWnd's default handler, which does not call update command UI handlers
for menu items. RESOLUTION Use the following steps to resolve this problem:
- Add an ON_WM_INITMENUPOPUP entry to the message map:
BEGIN_MESSAGE_MAP(CTestDlg, CDialog)
//}}AFX_MSG_MAP
ON_WM_INITMENUPOPUP()
END_MESSAGE_MAP()
- Add a OnInitMenuPopup member function to your dialog box class and copy the following
code (note that this code is taken largely from CFrameWnd::OnInitMenuPopup in
WinFrm.cpp):
void CTestDlg::OnInitMenuPopup(CMenu *pPopupMenu, UINT nIndex,BOOL bSysMenu)
{
ASSERT(pPopupMenu != NULL);
// Check the enabled state of various menu items.
CCmdUI state;
state.m_pMenu = pPopupMenu;
ASSERT(state.m_pOther == NULL);
ASSERT(state.m_pParentMenu == NULL);
// Determine if menu is popup in top-level menu and set m_pOther to
// it if so (m_pParentMenu == NULL indicates that it is secondary popup).
HMENU hParentMenu;
if (AfxGetThreadState()->m_hTrackingMenu == pPopupMenu->m_hMenu)
state.m_pParentMenu = pPopupMenu; // Parent == child for tracking popup.
else if ((hParentMenu = ::GetMenu(m_hWnd)) != NULL)
{
CWnd* pParent = this;
// Child windows don't have menus--need to go to the top!
if (pParent != NULL &&
(hParentMenu = ::GetMenu(pParent->m_hWnd)) != NULL)
{
int nIndexMax = ::GetMenuItemCount(hParentMenu);
for (int nIndex = 0; nIndex < nIndexMax; nIndex++)
{
if (::GetSubMenu(hParentMenu, nIndex) == pPopupMenu->m_hMenu)
{
// When popup is found, m_pParentMenu is containing menu.
state.m_pParentMenu = CMenu::FromHandle(hParentMenu);
break;
}
}
}
}
state.m_nIndexMax = pPopupMenu->GetMenuItemCount();
for (state.m_nIndex = 0; state.m_nIndex < state.m_nIndexMax;
state.m_nIndex++)
{
state.m_nID = pPopupMenu->GetMenuItemID(state.m_nIndex);
if (state.m_nID == 0)
continue; // Menu separator or invalid cmd - ignore it.
ASSERT(state.m_pOther == NULL);
ASSERT(state.m_pMenu != NULL);
if (state.m_nID == (UINT)-1)
{
// Possibly a popup menu, route to first item of that popup.
state.m_pSubMenu = pPopupMenu->GetSubMenu(state.m_nIndex);
if (state.m_pSubMenu == NULL ||
(state.m_nID = state.m_pSubMenu->GetMenuItemID(0)) == 0 ||
state.m_nID == (UINT)-1)
{
continue; // First item of popup can't be routed to.
}
state.DoUpdate(this, TRUE); // Popups are never auto disabled.
}
else
{
// Normal menu item.
// Auto enable/disable if frame window has m_bAutoMenuEnable
// set and command is _not_ a system command.
state.m_pSubMenu = NULL;
state.DoUpdate(this, FALSE);
}
// Adjust for menu deletions and additions.
UINT nCount = pPopupMenu->GetMenuItemCount();
if (nCount < state.m_nIndexMax)
{
state.m_nIndex -= (state.m_nIndexMax - nCount);
while (state.m_nIndex < nCount &&
pPopupMenu->GetMenuItemID(state.m_nIndex) == state.m_nID)
{
state.m_nIndex++;
}
}
state.m_nIndexMax = nCount;
}
}
STATUS This behavior is by design. REFERENCES
For more information, click the following article number to view the article in the Microsoft Knowledge Base:
141751
Adding control bars to dialog boxes in MFC
Modification Type: | Major | Last Reviewed: | 1/10/2006 |
---|
Keywords: | kbtshoot kbDlg kbMenu kbprb KbUIDesign KB242577 kbAudDeveloper |
---|
|