对话框中使用ON_UPDATE_COMMAND_UI更新菜单

本文详细介绍了在MFC应用程序中如何利用ON_UPDATE_COMMAND_UI消息响应来动态更新对话框中的菜单项状态,确保用户界面的实时反馈。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 

症状

从命令用户界面处理函数(Command UI handler)改变菜单状态(启用/禁用,选择/取消选

择,更改文字)在由对话框处理时没有正常工作。

 
  1. void CTestDlg::OnUpdateFileExit(CCmdUI* pCmdUI    
  2. {    
  3.     pCmdUI->Enable(FALSE); //没有显示为禁用.    
  4.     pCmdUI->SetCheck(TRUE); // 没有文字前显示选定标记.    
  5.     pCmdUI->SetRadio(TRUE); // 没有在文字前显示点.    
  6.     pCmdUI->SetText("Close"); //没有更改菜单文字.    
  7. }   

原因

在下拉菜单显示的时候, WM_INITMENUPOPUP消息被先发送以显示菜单项。MFC CFrameWn

d::OnInitMenuPopup 函数遍历菜单项并为每个菜单项调用更新命令处理函数(如果有的

话).菜单的外观被更新以反映它的状态(启用/禁用,选择/取消选择)

更新用户界面机制在基于对话框的应用程序中不能工作,因为CDialog没有OnInitMenuP

opup 处理函数,而使用CWnd's 默认处理函数,该函数没有为菜单项调用更新命令处理函

数。

解决

适用下列步骤解决此问题

在消息映射中添加ON_WM_INITMENUPOPUP 项:

 
  1. BEGIN_MESSAGE_MAP(CTestDlg, CDialog)    
  2.  //{{AFX_MSG_MAP(CTestDlg)    
  3.                         ........................    
  4.                         ........................    
  5.  //}}AFX_MSG_MAP    
  6.  ON_WM_INITMENUPOPUP()    
  7. END_MESSAGE_MAP()   

在你的对话框类中添加OnInitMenuPopup成员函数且复制下列代码到该函数(注意:代码

基本上是从CFrameWnd::OnInitMenuPopup(在WinFrm.cpp中)复制过来的):

 
  1. void CTestDlg::OnInitMenuPopup(CMenu *pPopupMenu, UINT nIndex,BOOL bSysMenu)    
  2.      
  3. {    
  4.     ASSERT(pPopupMenu != NULL);    
  5.     // Check the enabled state of various menu items.    
  6.     CCmdUI state;    
  7.     state.m_pMenu = pPopupMenu;    
  8.     ASSERT(state.m_pOther == NULL);    
  9.     ASSERT(state.m_pParentMenu == NULL);    
  10.     // Determine if menu is popup in top-level menu and set m_pOther to    
  11.     // it if so (m_pParentMenu == NULL indicates that it is secondary popup)    
  12. .    
  13.     HMENU hParentMenu;    
  14.     if (AfxGetThreadState()->m_hTrackingMenu == pPopupMenu->m_hMenu)    
  15.         state.m_pParentMenu = pPopupMenu;    // Parent == child for tracking    
  16.  popup.    
  17.     else if ((hParentMenu = ::GetMenu(m_hWnd)) != NULL)    
  18.     {    
  19.         CWnd* pParent = this;    
  20.            // Child windows don't have menus--need to go to the top!    
  21.         if (pParent != NULL &&    
  22.            (hParentMenu = ::GetMenu(pParent->m_hWnd)) != NULL)    
  23.         {    
  24.            int nIndexMax = ::GetMenuItemCount(hParentMenu);    
  25.            for (int nIndex = 0; nIndex < nIndexMax; nIndex++)    
  26.            {    
  27.             if (::GetSubMenu(hParentMenu, nIndex) == pPopupMenu->m_hMenu)    
  28.             {    
  29.                 // When popup is found, m_pParentMenu is containing menu.    
  30.                 state.m_pParentMenu = CMenu::FromHandle(hParentMenu);    
  31.                 break;    
  32.             }    
  33.            }    
  34.         }    
  35.     }    
  36.     state.m_nIndexMax = pPopupMenu->GetMenuItemCount();    
  37.     for (state.m_nIndex = 0; state.m_nIndex < state.m_nIndexMax;    
  38.       state.m_nIndex++)    
  39.     {    
  40.         state.m_nID = pPopupMenu->GetMenuItemID(state.m_nIndex);    
  41.         if (state.m_nID == 0)    
  42.            continue// Menu separator or invalid cmd - ignore it.    
  43.         ASSERT(state.m_pOther == NULL);    
  44.         ASSERT(state.m_pMenu != NULL);    
  45.         if (state.m_nID == (UINT)-1)    
  46.         {    
  47.            // Possibly a popup menu, route to first item of that popup.    
  48.            state.m_pSubMenu = pPopupMenu->GetSubMenu(state.m_nIndex);    
  49.            if (state.m_pSubMenu == NULL ||    
  50.             (state.m_nID = state.m_pSubMenu->GetMenuItemID(0)) == 0 ||    
  51.             state.m_nID == (UINT)-1)    
  52.            {    
  53.             continue;       // First item of popup can't be routed to.    
  54.            }    
  55.            state.DoUpdate(this, TRUE);   // Popups are never auto disabled.    
  56.         }    
  57.         else    
  58.         {    
  59.            // Normal menu item.    
  60.            // Auto enable/disable if frame window has m_bAutoMenuEnable    
  61.            // set and command is _not_ a system command.    
  62.            state.m_pSubMenu = NULL;    
  63.            state.DoUpdate(this, FALSE);    
  64.         }    
  65.         // Adjust for menu deletions and additions.    
  66.         UINT nCount = pPopupMenu->GetMenuItemCount();    
  67.         if (nCount < state.m_nIndexMax)    
  68.         {    
  69.            state.m_nIndex -= (state.m_nIndexMax - nCount);    
  70.            while (state.m_nIndex < nCount &&    
  71.             pPopupMenu->GetMenuItemID(state.m_nIndex) == state.m_nID)    
  72.            {    
  73.             state.m_nIndex++;    
  74.            }    
  75.         }    
  76.         state.m_nIndexMax = nCount;    
  77.     }    
  78. }   

状态设计上行为如此。

更多信息

命令用户界面处理函数也被CWnd::OnCommand 调用以确认命令在传递之前没有被禁用。

这就是禁用的菜单项的命令处理没有被调用的原因(虽然没有以灰色显示(不可用))。

在这种情况下,菜单项没有被重画以反映菜单项的状态.这是Wincore.cpp 文件中的相关

代码:

   //在传递命令之前,确定命令没有被禁用

 
  1. CTestCmdUI state;   
  2. state.m_nID = nID;    
  3. OnCmdMsg(nID, CN_UPDATE_COMMAND_UI, &state, NULL);    
  4. if (!state.m_bEnabled)    
  5. {   
  6.     TRACE1("Warning: not executing disabled command %d/n", nID);    
  7.     return TRUE;    
  8. }   

重现此行为的步骤

使用应用程序向导建立一个基于对话框的应用程序

建立一个新的菜单资源,并且向其上添加文件和文件/退出菜单项。

在对话框的属性中设置对话框的菜单为上述菜单.

使用类向导为文件/退出菜单项添加一个UPDATE_COMMAND_UI处理并添加下列语句之一到

处理函数。

 
  1. pCmdUI->Enable(FALSE); //没有显示为禁用.    
  2. pCmdUI->SetCheck(TRUE); // 没有文字前显示选定标记.    
  3. pCmdUI->SetRadio(TRUE); // 没有在文字前显示点.    
  4. pCmdUI->SetText("Close"); //没有更改菜单文字.   

编译运行此程序。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值