MFC自绘Menu的Bug

最近在使用MFC自绘菜单时遇到一个问题:当菜单项都为Popup Menu 时,发现MFC不会调用自绘MeasureItem,

以至于我的菜单宽度不对,无法显示菜单项文字。

跟踪发现: 在wincore.cpp中的OnMeasureItem函数中调用的_AfxFindPopupMenuFromID有点问题。OnMeasureItem是自绘菜单弹出时向菜单父窗口发送的WM_MEASUREITEM消息的响应函数,而_AfxFindPopupMenuFromID是个递归查找子菜单的函数,如果AfxFindPopupMenuFromID找到子菜单MFC就会调用菜单的MeasureItem。(wincore.cpp在vc安装目录下atlmfc/src/mfc目录中)

 _AfxFindPopupMenuFromID的代码如下:

AFX_STATIC CMenu *  AFXAPI _AfxFindPopupMenuFromID(CMenu *  pMenu, UINT nID)
{
    ENSURE_VALID(pMenu);
    
//  walk through all items, looking for ID match
    UINT nItems  =  pMenu -> GetMenuItemCount();
    
for  ( int  iItem  =   0 ; iItem  <  ( int )nItems; iItem ++ )
    {
        CMenu
*  pPopup  =  pMenu -> GetSubMenu(iItem);
        
if  (pPopup  !=  NULL)
        {
            
if (pPopup -> m_hMenu  ==  (HMENU)(UINT_PTR)nID) {
                pMenu 
=  CMenu::FromHandlePermanent(pMenu -> m_hMenu);
                
return  pMenu;
            }
            
//  recurse to child popup
            pPopup  =  _AfxFindPopupMenuFromID(pPopup, nID);
            
//  check popups on this popup
             if  (pPopup  !=  NULL)
                
return  pPopup;
        }
        
else   if  (pMenu -> GetMenuItemID(iItem)  ==  nID)
        {
            
//  it is a normal item inside our popup
            pMenu  =  CMenu::FromHandlePermanent(pMenu -> m_hMenu);
            
return  pMenu;
        }
    }
    
//  not found
     return  NULL;
}

可以看出这条函数只检查了pMenu的子菜单或菜单项而没有检查pMenu自身,所以当自绘菜单的菜单项只有子菜单就会导致查找不到菜单而不调用自绘菜单的MeasureItem函数。

修改方法: 可以在菜单的父窗口响应WM_MEASUREITEM消息,具体修改如下

CMenu *   MyFindPopupMenuFromID(CMenu *  pMenu, UINT nID)
{
    ENSURE_VALID(pMenu);
    
//  walk through all items, looking for ID match
    UINT nItems  =  pMenu -> GetMenuItemCount();
    
for  ( int  iItem  =   0 ; iItem  <  ( int )nItems; iItem ++ )
    {
        CMenu
*  pPopup  =  pMenu -> GetSubMenu(iItem);
        
if  (pPopup  !=  NULL)
        {
            
if (pPopup -> m_hMenu  ==  (HMENU)(UINT_PTR)nID) {
                pMenu 
=  CMenu::FromHandlePermanent(pMenu -> m_hMenu);
                
return  pMenu;
            }
            
//  recurse to child popup
            pPopup  =  MyFindPopupMenuFromID(pPopup, nID);
            
//  check popups on this popup
             if  (pPopup  !=  NULL)
                
return  pPopup;
        }
        
else   if  (pMenu -> GetMenuItemID(iItem)  ==  nID)
        {
            
//  it is a normal item inside our popup
            pMenu  =  CMenu::FromHandlePermanent(pMenu -> m_hMenu);
            
return  pMenu;
        }
        
if (pMenu -> GetSafeHmenu()  ==  nID)
        {
            pMenu 
=  CMenu::FromHandlePermanent(pMenu -> m_hMenu);
            
return  pMenu;            
        }
    }
    
//  not found
     return  NULL;
}

void  CMyWnd::OnMeasureItem( int   /* nIDCtl */ , LPMEASUREITEMSTRUCT lpMeasureItemStruct)
{
    
if  (lpMeasureItemStruct -> CtlType  ==  ODT_MENU)
    {
        ASSERT(lpMeasureItemStruct
-> CtlID  ==   0 );
        CMenu
*  pMenu = NULL;

        _AFX_THREAD_STATE
*  pThreadState  =  _afxThreadState.GetData();
        
if  (pThreadState -> m_hTrackingWindow  ==  m_hWnd)
        {
            
//  start from popup
            pMenu  =  CMenu::FromHandle(pThreadState -> m_hTrackingMenu);
        }
        
else
        {
            
//  start from menubar
            pMenu  =  GetMenu();
        }

        ENSURE(pMenu);

        pMenu 
=  MyFindPopupMenuFromID(pMenu, lpMeasureItemStruct -> itemID);
        
if  (pMenu  !=  NULL)
        {
            pMenu
-> MeasureItem(lpMeasureItemStruct);
        }
        
else
        {
            TRACE(traceAppMsg, 
0 " Warning: unknown WM_MEASUREITEM for menu item 0x%04X. " ,
                lpMeasureItemStruct
-> itemID);
        }
    }
    
else
    {
        CWnd
*  pChild  =  GetDescendantWindow(lpMeasureItemStruct -> CtlID, TRUE);
        
if  (pChild  !=  NULL  &&  pChild -> SendChildNotifyLastMsg())
            
return ;      //  eaten by child
    }
    
//  not handled - do default
    Default();
}

可以看出其实只在查找函数中多加了一个判断。至于为什么用HMENU和id比较,是因为如果是子菜单的话,id就是子菜单的句柄。

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值