症状:
从命令用户界面处理函数(Command UI handler)改变菜单状态(启用/禁用,选择/取消选择,更改文字)在由对话框处理时没有正常工作。
void CTestDlg::OnUpdateFileExit(CCmdUI* pCmdUI
{
pCmdUI->Enable(FALSE); //没有显示为禁用.
pCmdUI->SetCheck(TRUE); // 没有文字前显示选定标记.
pCmdUI->SetRadio(TRUE); // 没有在文字前显示点.
pCmdUI->SetText("Close"); //没有更改菜单文字.
}
原因:
在下拉菜单显示的时候, WM_INITMENUPOPUP消息被先发送以显示菜单项。MFC CFrameWnd::OnInitMenuPopup 函数遍历菜单项并为每个菜单项调用更新命令处理函数(如果有的 话).菜单的外观被更新以反映它的状态(启用/禁用,选择/取消选择)更新用户界面机制在基于对话框的应用程序中不能工作,因为CDialog没有OnInitMenuPopup 处理函数,而使用CWnd's 默认处理函数,该函数没有为菜单项调用更新命令处理函 数。
解决方法:
适用下列步骤解决此问题
1、在消息映射中添加ON_WM_INITMENUPOPUP项
2、在你的对话框类中添加OnInitMenuPopup成员函数且复制下列代码到该函数(注意:代码 基本上是从CFrameWnd::OnInitMenuPopup(在WinFrm.cpp中)复制过来的);
void CThjCtrlWnd::OnInitMenuPopup(CMenu* pPopupMenu, UINT nIndex, BOOL bSysMenu)
{
CDialog::OnInitMenuPopup(pPopupMenu, nIndex, bSysMenu);
// TODO: 在此处添加消息处理程序代码
if (bSysMenu)
return;
//ASSERT(pPopupMenu != NULL);
ENSURE_VALID(pPopupMenu);
// 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 = GetTopLevelParent();
// 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 nItemIndex = 0; nItemIndex < nIndexMax; nItemIndex++)
{
if (::GetSubMenu(hParentMenu, nItemIndex) == 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, FALSE); // 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;
}
}