完美实现真彩自绘菜单
作者:阿福(geforce_zf)
下载源代码
一、提出问题
在VCKBASE上读到《自绘菜单的实现》[作者:querw],
完美实现真彩自绘菜单
。应用的我自己的正在进行的工程后发现效果不错,可是有存在许多问题。整个类的设计方面存在很多缺陷(先天,后天的),存在的主要问题如下:当应用在多文档界面(MDI)中的时候,无法对系统自动添加菜单和文档模板菜单进行自绘(比如无法对文件->最近文件(MRU)菜单项中的文件列表就是系统自动添加)。原因是类内部没有对CMainFrame.:OnInitPopupMenu()消息进行处理的函数, 因此不具备修改系统自动添加菜单项的功能。(BCMENU有这功能,而且工作的不错)
作者提到的 BCMENU 不用映射 WM_DRAWITEM 和 WM_MEASUREITEM 两个消息就能实现自画功能,实际上是错误的。不映射这两个重要的消息,即使能自绘,也是有问题的,不信看图。
菜单编辑器中的模菜单样
使用BCMENU并且映射了这两个消息后的执行情况
使用BCMENU没有映射两个消息的执行情况
原作者分析的自绘的是因为把主菜单(top-level menu)的子菜单都加载成弹出菜单(popupmenu),是不正确的。真正的原因是因为MFC框架会自动调用CMenu的两个虚拟函数MeasureItem()和OnDrawItem()。 因此,当CMenuEx派生于CMenu,并且重写这两个虚拟函数以后。
1、MFC框架调用的GetMenu()->MeasureItem()就相当于调用了CMenuEx::MeasureItem(),从而实现自绘菜单控件尺寸的测量。2、MFC框架调用GetMenu()->DrawItem()就相当于调用了CMenuEx::DrawItem()来实现自绘菜单控件的自绘操作(不懂??,这正是C++的虚拟的妙用,指向派生类对象的基类指针可以调用派生类的虚拟函数,多么伟大的发明,谁想出来的???)。与子菜单是否为弹出菜单(popupmenu)没有什么关系,
电脑资料
《完美实现真彩自绘菜单》(https://www.unjs.com)。以下是摘自WINCORE.CPP的一段程序,也就是WM_MEASUREITEM消息的默认流向的地方,相信大家会从中看出一些端倪。void CWnd::OnMeasureItem(int /*nIDCtl*/, LPMEASUREITEMSTRUCT lpMeasureItemStruct){ if (lpMeasureItemStruct->CtlType == ODT_MENU) { ...... // 如果没有主菜单 if (pThreadState->m_hTrackingWindow == m_hWnd) { ...... } else { // 如果有主菜单 pMenu = GetMenu(); // 找到窗体的主菜单,注意,pMenu的是CMenu* 类型 } // 在当前菜单中寻找ID匹配的菜单项 pMenu = _AfxFindPopupMenuFromID(pMenu, lpMeasureItemStruct->itemID); if (pMenu != NULL) // 如果找到,就调用MeasureItem() // 这就是所谓的基类指针指向派生类对象,可以调用派生类虚拟函数的情况了 pMenu->MeasureItem(lpMeasureItemStruct); else TRACE1("Warning: unknown WM_MEASUREITEM for menu item 0x%04X.\n", lpMeasureItemStruct->itemID); } else { ...... } ......}
当菜单项中含有子菜单(submenu),而不含有分割条的时候,子菜单项的高度不可调。原因为原CMenuEx程序中将分割条的原COMMAND ID(0)改为菜单项的COMMADN ID(-1), 以欺骗MFC框架调用CMenuEx::MeasureItem()来计算子菜单项(submenu)的高度。(很令我失望,这也是促使我自己动手重写该类的原因之一。不信看程序,看图)
摘录自原CMenuEx.cpp第546-560行
if(uID == 0) //分隔符{ ::AppendMenu(hNewMenu,MF_SEPARATOR,0,NULL); ...... // 注意,就是下面那个-1,把分割条的ID从0改到-1, // 从而是MFC框架误以为找到了ID为-1的菜单项,并且测量了它的尺寸 // 而实际上ID为-1的菜单项是不可能被void CWnd::OnMeasureItem()找到的 ::ModifyMenu(hNewMenu,i,MF_BYPOSITION | MF_OWNERDRAW,-1,(LPCTSTR)pMenuItem);}菜单编辑器中没有分割条菜单的菜单
原CMenuEx执行的模样