MFC自绘Menu的Bug

原创 2007年10月15日 23:55:00

最近在使用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就是子菜单的句柄。

自绘菜单

完美实现真彩自绘菜单作者:阿福(geforce_zf)下载源代码一、提出问题   在VCKBASE上读到《自绘菜单的实现》[作者:querw]。应用的我自己的正在进行的工程后发现效果不错,可是有存在许...
  • caowei880123
  • caowei880123
  • 2011年03月10日 11:55
  • 2263

MFC中窗口重绘

在刷新窗口时经常要调用重绘函数 MFC提供了三个函数用于窗口重绘 InvalidateRect(&Rect) Invalidate() UpdateWindow...
  • qingzai_
  • qingzai_
  • 2016年07月22日 14:56
  • 859

MFC-自绘控件(CComBox篇)

示例图 一、公共文件 和这篇一样,就不写了(MFC-自绘控件(CButton篇)) 二、CComBox 头文件 #pragma once #include "Public.h" #include ...
  • wyansai
  • wyansai
  • 2016年03月29日 00:44
  • 1715

MFC Menu操作知识点解析

对于Menu的操作,主要经验及知识点总结如下。 1.
  • Andrewniu
  • Andrewniu
  • 2015年12月11日 14:12
  • 1321

MFC自绘入门之(三)

OnPaint是WM_PAINT消息的消息处理函数,在OnPaint中调用OnDraw,一般来说,用户自己的绘图代码应放在OnDraw中。 OnPaint()是CWnd的类成员,负责响应WM_P...
  • feixiangyue
  • feixiangyue
  • 2014年01月22日 10:37
  • 1247

MFC 之 CButton 控件重绘(GDI篇)

最近在为公司用MFC做产品界面。因为是小公司,所以也没有xi
  • yuzhenxiong0823
  • yuzhenxiong0823
  • 2014年04月09日 17:24
  • 9676

MFC-自绘控件(CEdit篇)

示例图(可以设置左右图标,字体颜色,圆角) 一、公共文件 和这篇一样,就不写了(MFC-自绘控件(CButton篇)) 二、CEdit 头文件 #pragma once #include "Publ...
  • wyansai
  • wyansai
  • 2016年03月27日 23:55
  • 2599

MFC-自绘控件(CStatic与CEdit)

一、CStatic 主要是在主窗口中的OnCtlColor函数中进行自绘 HBRUSH CSelfDrawDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCt...
  • wyansai
  • wyansai
  • 2016年03月26日 09:20
  • 1356

MFC-自绘贴图按钮

前言 控件窗口自绘贴图需要子类化, 有自绘风格, 接管WM_PAINT, 覆盖虚函数DrawItem. 效果 工程下载 srcSelfDrawBtn.zip 工程预览 调用方代...
  • LostSpeed
  • LostSpeed
  • 2016年03月15日 11:21
  • 849

MFC原创教程:3.0 Menu

依然是创建MFC单文档应用程序。 添加一个菜单选项: 右键属性,修改ID为IDM_TEST。//若无法修改,把属性--Popup修改为False即可。 点击--项目--类向导,分别在类名...
  • wowkk
  • wowkk
  • 2013年01月11日 11:25
  • 6453
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:MFC自绘Menu的Bug
举报原因:
原因补充:

(最多只允许输入30个字)