MFC 基于对话框的程序弹出菜单



转自:对话框中使用ON_UPDATE_COMMAND_UI更新菜单

从命令用户界面处理函数(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. }   
void CTestDlg::OnUpdateFileExit(CCmdUI* pCmdUI { pCmdUI->Enable(FALSE); //没有显示为禁用. pCmdUI->SetCheck(TRUE); // 没有文字前显示选定标记. pCmdUI->SetRadio(TRUE); // 没有在文字前显示点. pCmdUI->SetText("Close"); //没有更改菜单文字. }

原因

在下拉菜单显示的时候, 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()   
BEGIN_MESSAGE_MAP(CTestDlg, CDialog) //{{AFX_MSG_MAP(CTestDlg) ........................ ........................ //}}AFX_MSG_MAP ON_WM_INITMENUPOPUP() 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.    
  9.     state.m_nIndexMax = pPopupMenu->GetMenuItemCount();    
  10.     for (state.m_nIndex = 0; state.m_nIndex < state.m_nIndexMax;    
  11.       state.m_nIndex++)    
  12.     {    
  13.         state.m_nID = pPopupMenu->GetMenuItemID(state.m_nIndex);    
  14.         if (state.m_nID == 0)    
  15.            continue; // Menu separator or invalid cmd - ignore it. 
  16.         state.m_pSubMenu = NULL;    
  17.         state.DoUpdate(this, FALSE);    

  18.         // Adjust for menu deletions and additions.    
  19.         UINT nCount = pPopupMenu->GetMenuItemCount();    
  20.         if (nCount < state.m_nIndexMax)    
  21.         {    
  22.            state.m_nIndex -= (state.m_nIndexMax - nCount);    
  23.            while (state.m_nIndex < nCount &&    
  24.             pPopupMenu->GetMenuItemID(state.m_nIndex) == state.m_nID)    
  25.            {    
  26.             state.m_nIndex++;    
  27.            }    
  28.         }    
  29.         state.m_nIndexMax = nCount;    
  30.     }    
  31. }   

状态

设计上行为如此。

更多信息

命令用户界面处理函数也被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处理并添加下列语句之一到

处理函数。

===================================================================================================
弹出菜单更新方法

void CAVCDlg::OnRButtonUp(UINT nFlags, CPoint point)
{
    // TODO: Add your message handler code here and/or call default
    //定位鼠标的位置
    CPoint m_pPoint;             // A copy of the mouse position
    m_pPoint = point;
    ClientToScreen(&m_pPoint);// Convert the position to a screen position
   
    CMenu m_rMenu;
    m_rMenu.LoadMenu(IDR_MNUR );
    CMenu* pMenu=m_rMenu.GetSubMenu(0);

    //更新菜单状态
    ::SendMessage(this->m_hWnd,WM_INITMENUPOPUP,int(pMenu->m_hMenu),FALSE);
    pMenu->TrackPopupMenu(TPM_LEFTALIGN|TPM_RIGHTBUTTON,m_pPoint.x,m_pPoint.y,this);

    CDialog::OnRButtonUp(nFlags, point);
}

捕获WM_INITMENUPOPUP消息,在此函数更新菜单状态
void CAVCDlg::OnInitMenuPopup(CMenu* pPopupMenu, UINT nIndex, BOOL bSysMenu)
{
    CDialog::OnInitMenuPopup(pPopupMenu, nIndex, bSysMenu);
   
    // TODO: Add your message handler code here
    if(nIndex)        //
    { 
        CDialog::OnInitMenuPopup(pPopupMenu,   nIndex,   bSysMenu); 
        return; 
    } 
    ASSERT(pPopupMenu); 
    pPopupMenu->EnableMenuItem(0,MF_BYPOSITION|MF_DISABLED|MF_GRAYED);      
}

PS:

工作原理可以参考:CCmdUI工作原理及作用

你可能感兴趣的:(menu,对话框,C++,Programming)