完美实现真彩自绘菜单
作者:阿福(geforce_zf)
下载源代码
一、提出问题
在VCKBASE上读到《自绘菜单的实现》[作者:querw]。应用的我自己的正在进行的工程后发现效果不错,可是有存在许多问题。整个类的设计方面存在很多缺陷(先天,后天的),存在的主要问题如下:
使用BCMENU并且映射了这两个消息后的执行情况
使用BCMENU没有映射两个消息的执行情况
原作者分析的自绘的是因为把主菜单(top-level menu)的子菜单都加载成弹出菜单(popupmenu),是不正确的。真正的原因是因为MFC框架会自动调用CMenu的两个虚拟函数MeasureItem()和OnDrawItem()。 因此,当CMenuEx派生于CMenu,并且重写这两个虚拟函数以后。
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 { ...... } ...... }
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); }菜单编辑器中没有分割条菜单的菜单
代码不够简练,程序粒度划分不好,可读性差(不过比BCMENU的代码可读性强多了:))。
二、解决问题
针对以上遇到的问题,我参考BCMENU和原作者的CMenuEx,对CMenuEx类重新进行了组织,类定义如下:
// 声明,因为下面的结构要用到 CMenuEx*,又不支持向后引用,又什么办法啊! class CMenuEx; //自绘菜单数据项结构,就是要传给系统的那个牛X的LPCTSTR指针所指向的东东 class CMenuEx : public CMenu { DECLARE_DYNAMIC( CMenuEx ) // Constructor public: CMenuEx(); virtual ~CMenuEx(); virtual BOOL DestroyMenu(); // Operation public: // 加载菜单操作 BOOL LoadMenu(UINT nIDResource); BOOL LoadMenu(LPCTSTR lpszResourceName); BOOL LoadMenu(HMENU hMenu); BOOL LoadMenu(CMenu & Menu); // 菜单项操作,如果当前菜单为主菜单(top-level)就调用相应的CMenu的操作。如果是弹出菜单, // 就将新加入的菜单项定义为自绘菜单 BOOL AppendMenu(UINT nFlags, UINT nIDNewItem = 0,LPCTSTR lpszNewItem = NULL); BOOL InsertMenu(UINT nPosition,UINT nFlags,UINT nIDNewItem=0,LPCTSTR lpszNewItem=NULL ); BOOL ModifyMenu(UINT nPosition,UINT nFlags,UINT nIDNewItem=0,LPCTSTR lpszNewItem=NULL ); BOOL RemoveMenu(UINT nPosition, UINT nFlags); // 加载菜单图像操作 //通过菜单索引表加载图像索引,此操作必须在设置过菜单图像后调用 void SetImageIndex(const UINT* nIDResource,UINT nIDCount); void LoadToolBar(const CToolBar* pToolBar);// 通过工具栏加载图像,和图像索引 // 取自绘菜单项的数据项 UINT GetMenuItemSize() const; LPMENUITEM GetMenuItem(UINT nPosition); // 取子菜单操作,如果位置nPosition存在子菜单,返回该子菜单指针 // 如果不存在子菜单,返回NULL CMenuEx* GetSubMenu(int nPosition); // 在当前菜单和所以子菜单中中寻找相应ID // 如果找到,返回ID所在菜单的指针,没找到返回NULL CMenuEx* FindPopupMenuFromID(UINT nID); // Attributes protected: // 指示为主菜单(top-level menu or menubar)还是弹出菜单(popupmenu) BOOL m_bPopupMenu; // 分割条的默认高度 int m_nSeparator; // 绘制菜单需要的颜色 COLORREF m_crBackground; // 菜单背景色 COLORREF m_crTextSelected; // 菜单项被选中时的文字颜色 COLORREF m_crText; // 菜单项文字颜色 COLORREF m_crLeft; // 菜单左侧的背景颜色 COLORREF m_crSelectedBroder; // 菜单选中框的线条颜色 COLORREF m_crSelectedFill; // 菜单选中框的填充颜色 // 菜单项图像的尺寸 CSize m_szImage; CImageList* m_pImageList; // 菜单项正常的图像列表 CImageList* m_pDisabledImageList; // 菜单项禁用时的图像列表 CImageList* m_pHotImageList; // 菜单项被选中时的图像列表 protected: // 包含所有菜单项的数组 CArray m_MenuItemArr; public: // 设置颜色操作 void SetTextSelectedColor(COLORREF color); void SetBackgroundColor(COLORREF color); void SetTextColor(COLORREF color); void SetLeftColor(COLORREF color); void SetSelectedBroderColor(COLORREF color); void SetSelectedFillColor(COLORREF color); // 设置图像列表操作 void SetImageList(CImageList* pImageList); void SetDisabledImageList(CImageList* pImageList); void SetHotImageList(CImageList* pImageList); // 设置当前菜单为主菜单还是弹出菜单 void SetPopupMenu(BOOL bPopupMenu); // Implementation public: // 绘制菜单项的虚拟函数,由MFC框架自动调用 virtual void DrawItem(LPDRAWITEMSTRUCT lpDIS); // 更新弹出菜单菜单项操作 // 因为有时候系统会通过菜单句柄插入一些非自绘菜单 // 该函数就是更新这些非自绘菜单为自绘菜单 void UpdatePopupMenu(); protected: // 绘制菜单项的辅助函数,想自己的菜单看上去更COOL,就拿他们开刀 void DrawBackground(CDC* pDC,CRect rect); void DrawMenuImage(CDC* pDC,CRect rect,LPDRAWITEMSTRUCT lpDIS); void DrawMenuText(CDC* pDC,CRect rect,LPDRAWITEMSTRUCT lpDIS); void DrawSelected(CDC* pDC,CRect rect,LPDRAWITEMSTRUCT lpDIS); // Static Member public: // 在CMainFrame的OnMeasureItem()消息映射函数中调用它,用来测量所有菜单项尺寸 static void MeasureItem(LPMEASUREITEMSTRUCT lpMIS); // 在CMainFrame的OnInitPopupMenu()消息映射函数中调用它, // 用来更新系统自动添加的菜单项为自绘菜单 static void InitPopupMenu(CMenu* pPopupMenu,UINT nIndex,BOOL bSystem); }; #endif // !defined(MENUEX_H)三、实现方法
#include "MenuEx.h" // 添加头文件 class CMainFrame : public CMDIFrameWnd { ... public: HMENU InitMainFrameMenu(); // 初始化主菜单 HMENU InitImageTypeMenu(); // 初始化文档模板菜单 protected: // CMenuEx members CMenuEx m_menuMainFrame; // 主窗体没有打开任何文档时菜单 CMenuEx m_menuImageType; // 主窗体打开文档时菜单(文档模板菜单) protected: // CMenuEx''s image list members CImageList m_imageMenu; // 菜单项正常的图像列表 CImageList m_imageMenuDisable; // 菜单项禁用时的图像列表 CImageList m_imageMenuHot; // 菜单项被选中时的图像列表 ... }
// 声明,因为下面的结构要用到 CMenuEx*,又不支持向后引用,又什么办法啊! class CMenuEx; //自绘菜单数据项结构,就是要传给系统的那个牛X的LPCTSTR指针所指向的东东 typedef struct tagMENUITEM { CString strText; // 菜单名称 UINT nID; // 菜单ID号 // 分割条的ID是 0 // 子菜单的ID是 -1 CSize itemSize; // 菜单项的尺寸,不包括菜单图像的尺寸 CImageList* pImageList; // 菜单项的正常图像列表 CImageList* pDisabledImageList; // 菜单项的禁用图像列表 CImageList* pHotImageList; // 菜单项的选中图像列表 UINT nImageIndex; // 菜单项的图像列表索引,-1表示没有图像 BOOL bIsSubMenu; // 表示当前菜单项是否为子菜单项 CMenuEx* pSubMenu; // 如果是一般菜单,该值为NULL // 如果bIsSubMenu为TRUE,该值为指向子菜单项的CMenuEx*指针 } MENUITEM,*LPMENUITEM; /////////////////////////////////////////// // 在ManiFram.cpp 中添加菜单图像索引表 static UINT nMenuImageIndex[] = { ID_FILE_OPEN, ID_FILE_SAVE, ID_FILE_PRINT, ID_EDIT_COPY, ID_EDIT_PASTE, ID_EDIT_UNDO, ID_EDIT_REDO, ID_APP_ABOUT, ID_IMAGE_LEVEL, ID_IMAGE_EQUALIZE, ID_IMAGE_SMOOTH, ID_IMAGE_SHARP, ID_IMAGE_SIZE, ID_IMAGE_RA, ID_IMAGE_HISTOGRAM, ID_ZOOMOUT, ID_ZOOMIN, }; ///////////////////////////////////////////////////////////////////////////// // 在ManiFram.cpp 中添加初始化菜单程序 void CMainFrame::InitMenuImage() { // 初始化菜单图像列表 CBitmap bm; m_imageMenu.Create(20, 20, TRUE | ILC_COLOR24, 9, 0); // 要问我IDB_SMALLMENUCOLOR是什么,当然是是真彩位图了,看图说话了 bm.LoadBitmap(IDB_SMALLMENUCOLOR); m_imageMenu.Add(&bm,(CBitmap*)NULL); bm.Detach(); // 还有IDB_SMALLMENUDISABLE m_imageMenuDisable.Create(20, 20, TRUE | ILC_COLOR24, 9, 0); bm.LoadBitmap(IDB_SMALLMENUDISABLE); m_imageMenuDisable.Add(&bm,(CBitmap*)NULL); bm.Detach(); // 还有IDB_SMALLMENUHOT m_imageMenuHot.Create(20, 20, TRUE | ILC_COLOR24, 9, 0); bm.LoadBitmap(IDB_SMALLMENUHOT); m_imageMenuHot.Add(&bm,(CBitmap*)NULL); bm.Detach(); } /* IDB_SMALLMENUCOLOR IDB_SMALLMENUHOT IDB_SMALLMENUDISABLE当然,要通过资源编辑器的Import功能将他们导入到资源文件中,不过因为是真彩,所以不能用VC的图片编辑器编辑了。 告诉大家个敲门,我是用windows自带的画笔画的:)
*/ ///////////////////////////////////////////////////////////////////////////// // 在ManiFram.cpp 中添加初始化菜单图像列表程序 int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) { // 在CMainFrame::OnCreate中调用菜单图标初始化程序 。。。。。。 InitMenuImage(); 。。。。。。 } ///////////////////////////////////////////////////////////////////////////// HMENU CMainFrame::InitMainFrameMenu() { //初始化主菜单 m_menuMainFrame.LoadMenu(IDR_MAINFRAME); { // 这只加载图像的一种方法,是一种两步方法,先加载图像列表 m_menuMainFrame.SetImageList(&m_imageMenu); m_menuMainFrame.SetDisabledImageList(&m_imageMenuDisable); m_menuMainFrame.SetHotImageList(&m_imageMenuHot); // 再通过菜单图像索引表为菜单加载图像索引, m_menuMainFrame.SetImageIndex(nMenuImageIndex, sizeof(nMenuImageIndex)/sizeof(UINT)); } // 也可以使用另外一种一步方法加载图像 /* // 假设MAINFRAM具有m_wndToolBar成员,并且已经设置了真彩位图 // 关于设置工具栏的真彩位图,请参考 http://www.vckbase.com/document/viewdoc/?id=576 // 或者看我的另外一篇文章 《完美实现真彩工具栏》(还没写出来那:)) // 不过源程序里面已经有实现方法了 // 自己看也可以明白的 m_menuMainFrame.LoadToolBar(&m_wndToolBar); */ return m_menuMainFrame.Detach(); } ///////////////////////////////////////////////////////////////////////////// HMENU CMainFrame::InitImageTypeMenu() { // 初始化文档模板菜单 m_menuImageType.LoadMenu(IDR_IMAGETYPE); m_menuImageType.SetImageList(&m_imageMenu); m_menuImageType.SetDisabledImageList(&m_imageMenuDisable); m_menuImageType.SetHotImageList(&m_imageMenuHot); //通过菜单图像索引表为菜单加载图像索引 m_menuImageType.SetImageIndex(nMenuImageIndex,sizeof(nMenuImageIndex)/sizeof(UINT)); return m_menuImageType.Detach(); } ///////////////////////////////////////////////////////////////////////////// void CMainFrame::OnInitMenuPopup(CMenu* pPopupMenu, UINT nIndex, BOOL bSysMenu) { // 记住,顺序一定不能反,因为有些MFC自动添加的菜单是在CMDIFrameWnd::OnInitMenuPopup() // 中添加的. // 如果反了,当然就找不到新加入的菜单了 CMDIFrameWnd::OnInitMenuPopup(pPopupMenu, nIndex, bSysMenu); // 静态函数,看好了,别忘了写CMenuEx啊 CMenuEx::InitPopupMenu(pPopupMenu, nIndex, bSysMenu); } ///////////////////////////////////////////////////////////////////////////// void CMainFrame::OnMeasureItem(int nIDCtl, LPMEASUREITEMSTRUCT lpMeasureItemStruct) { // 都是她惹的祸"CMDIFrameWnd::OnMeasureItem()",不对子菜单项的尺寸进行测量 // 害的我们只好映射这个函数了 CMDIFrameWnd::OnMeasureItem(nIDCtl, lpMeasureItemStruct); // 静态函数,看好了,别忘了写CMenuEx啊 CMenuEx::MeasureItem(lpMeasureItemStruct); }
BOOL CXXXApp::InitInstance() { ...... CMultiDocTemplate* pDocTemplate; pDocTemplate = new CMultiDocTemplate( IDR_IMAGETYPE, RUNTIME_CLASS(CImageDoc), RUNTIME_CLASS(CChildFrame), // custom MDI child frame RUNTIME_CLASS(CImageView)); AddDocTemplate(pDocTemplate); // create main MDI Frame window CMainFrame* pMainFrame = new CMainFrame; if (!pMainFrame->LoadFrame(IDR_MAINFRAME)) return FALSE; m_pMainWnd = pMainFrame; // 这些才是要添加的代码,别弄错了 // 初始化文档模板菜单 pDocTemplate->m_hMenuShared=pMainFrame->InitImageTypeMenu(); // 初始化主窗体菜单 pMainFrame->m_hMenuDefault=pMainFrame->InitMainFrameMenu(); // 更新,具体干什么没研究,反正不调用就出错了:) pMainFrame->OnUpdateFrameMenu(pMainFrame->m_hMenuDefault); // 要添加的代码到这结束 ...... }
三、总结
说了这么多,也不知道大家看明白没有,没关系,先贴个图,大家看看效果再说了。
效果图一,使用图像索引表加载的小图标菜单
效果图一,工具条加载的大图标菜单
四、结束语
感谢querw和BCMenu的作者,没有他们的辛勤劳动,后人是没办法站在他们肩膀上的!由于程序写的匆忙,难免有不尽人意和错误的地方,欢迎大家任意修改源程序:) 要说这个菜单做的完美,那是吹牛,世界上哪有完美的东西啊 :) 只要自己觉得完美,就够了。 希望大家能从文章中学到点东西,就好。
自绘菜单的实现
作者:querw
(北方工业大学 2000级计算机4班)
下载源代码
在VCKBASE上读到《一种漂亮的自绘菜单》 [作者:郑恒 (lbird)]。应用到我的工程里后发现:文章中提到的效果能很好的实现。但是有一点不方便:需要映射 WM_DRAWITEM 和 WM_MEASUREITEM 消息才能实现自画功能。这对于一个基于对话框的工程或者仅仅需要弹出式菜单的工程来说很不方便。网上有一种很有名的自绘菜单 :BCMenu (http://www.rocscience.com/~corkum/BCMenu.html) (在附带工程中也有 BCMenu),在使用它的时候并不需要映射上述的两个消息就能实现自绘效果。这个问题让我觉得很困惑,MSDN也说明:MeasureItem() 和 DrawItem() 两个虚函数是由框架调用的 。并不用手工映射。可是若不映射上述的两个消息则显示不正常。(我查看了好多资料,直到现在还是不明白原因。呵呵:))既然 BCMenu 可以不用映射 WM_DRAWITEM 和 WM_MEASUREITEM 就能实现自画功能,那么它肯定经过了特殊处理。果然,BCMenu::LoadMenu()对整个菜单作了处理 。我注意到,如果菜单是弹出式的,那么不需要映射 WM_DRAWITEM 和 WM_MEASUREITEM 就能实现自画功能。于是我在CMenuEx::LoadMenu()中重新构建了整个菜单, 把所有的子菜单创建为弹出式的菜单使用API函数::CreatePopupMenu(),代码如下:
BOOL CMenuEx::LoadMenu(UINT uMenu) { //重新读入菜单,创建为popup菜单,才能自画(由框架调用MesureItem() 和 DrawItem() HMENU hMenu = ::CreateMenu(); this->Attach(hMenu); //临时菜单(使用CMenu的LoadMenu()函数读入菜单,并以之为蓝本构建新的菜单) CMenu Menu; UINT uID; Menu.LoadMenu(uMenu); for(int i = 0; i < (int)Menu.GetMenuItemCount(); i++) { uID = Menu.GetMenuItemID(i); if(uID == 0) //分隔符 { ::AppendMenu(hMenu,MF_SEPARATOR,0,NULL); } else if((int)uID == -1) //弹出菜单(即子菜单) { CMenu *pSubMenu = Menu.GetSubMenu(i); //创建子菜单 HMENU hSubMenu = ::CreatePopupMenu(); CString strPopup; Menu.GetMenuString(i,strPopup,MF_BYPOSITION); ::InsertMenu(hMenu, i, MF_BYPOSITION | MF_POPUP | MF_STRING, (UINT)hSubMenu, strPopup); //对子菜单递归调用ChangeMenuStyle(),把子菜单改为MF_OWNERDRAW风格 ChangeMenuStyle(pSubMenu,hSubMenu); } else //正常的菜单项 { CString strText; Menu.GetMenuString(uID,strText,MF_BYCOMMAND); AppendMenu(MF_STRING,uID,strText); } } Menu.DestroyMenu(); //销毁临时菜单 return TRUE; } void CMenuEx::ChangeMenuStyle(CMenu *pMenu,HMENU hNewMenu) { //关联为CMenuEx(关联为CMenuEx后才能自动重画 //原因不明(CMenu封装的结果?) CMenuEx *pNewMenu; pNewMenu = new CMenuEx; pNewMenu->Attach(hNewMenu); m_SubMenuArr.Add(pNewMenu); UINT uID; int nItemCount = pMenu->GetMenuItemCount(); for(int i = 0; i < nItemCount; i++) { uID = pMenu->GetMenuItemID(i); if(uID == 0) //分隔符 { ::AppendMenu(hNewMenu,MF_SEPARATOR,0,NULL); //pNewMenu->AppendMenu(MF_SEPARATOR,0,NULL); CString strText; MENUITEM *pMenuItem = new MENUITEM; pMenuItem->uID = 0; pMenuItem->uIndex = -1; pMenuItem->uPositionImageLeft = -1; pMenuItem->pImageList = &m_ImageList; m_MenuItemArr.Add(pMenuItem); ::ModifyMenu(hNewMenu, i, MF_BYPOSITION | MF_OWNERDRAW, -1, (LPCTSTR)pMenuItem); } else if(uID == -1) //弹出菜单(即子菜单) { CMenu *pSubMenu = pMenu->GetSubMenu(i); HMENU hPopMenu = ::CreatePopupMenu(); CString strPopup; pMenu->GetMenuString(i,strPopup,MF_BYPOSITION); ::InsertMenu(hNewMenu, i, MF_BYPOSITION | MF_POPUP, (UINT)hPopMenu, strPopup); MENUITEM *pMenuItem = new MENUITEM; pMenuItem->uID = -1; pMenuItem->strText = strPopup; pMenuItem->uIndex = -1; pMenuItem->uPositionImageLeft = -1; pMenuItem->pImageList = &m_ImageList; m_MenuItemArr.Add(pMenuItem); ::ModifyMenu(hNewMenu, i, MF_BYPOSITION | MF_OWNERDRAW, -1, (LPCTSTR)pMenuItem); ChangeMenuStyle(pSubMenu,hPopMenu); } else //正常的菜单项 { CString strText; pMenu->GetMenuString(uID,strText,MF_BYCOMMAND); MENUITEM *pMenuItem = new MENUITEM; pMenuItem->uID = pMenu->GetMenuItemID(i); pMenu->GetMenuString(pMenuItem->uID, pMenuItem->strText, MF_BYCOMMAND); pMenuItem->uIndex = -1; pMenuItem->uPositionImageLeft = -1; pMenuItem->pImageList = &m_ImageList; m_MenuItemArr.Add(pMenuItem); UINT uState = pMenu->GetMenuState(i,MF_BYPOSITION); ::AppendMenu(hNewMenu, MF_OWNERDRAW | MF_BYCOMMAND | uState, uID, (LPCTSTR)pMenuItem); } } }这样,利用标注的CMenu::LoadMenu()函数读入菜单,并根据这个菜单重新构建一个新的菜单,在新菜单中把所有的子菜单创建为弹出式菜单并关联一个CMenuEx类。根据需要,我提供了一个
CMenuEx::LoadToolBar(UINT uToolBar, UINT uFace)
接口,请注意它的两个参数:uToolBar 是工具条的资源,uFace 是一个替代位图的资源ID。因为VC6.0中做一个真彩工具栏并不是一件容易的事,所以我做了一个小动作:用IDE的资源编辑器随便编辑一个工具条,只要ID和菜单ID相对应即可,然后可以用外部编辑器编辑好真正要使用的位图(顺序和工具条资源的顺序一样),并把该位图作为uFace参数传入,菜单就可以有真彩图标了。
CMenuEx还提供了如下三个接口:
BOOL ModifyMenuEx() BOOL AppendMenuEx() BOOL RemoveMenuEx()
功能一目了然,只是增加了对自绘风格的处理,应用的时候只要像调用普通的CMenu::AppendMenu()等函数一样就自动拥有自绘风格了。我写这篇文章的目的在于提出菜单派生类调用 MeasureItem() 和 DrawItem()的问题。至于实现漂亮的菜单界面主要工作当然还是在 DrawItem() 函数中做,有特殊需要的可以自行定义 MENUITEM 结构,重新写 DrawItem() 函数。我没有提供设置菜单附加位图的具体代码,相信这个不是问题。你可以很容易的通过重写 DrawItem()实现。有必要提醒的是:有关一个菜单项的信息最好能完全从一个MENUITEM结构中取得,使
virtual void MeasureItem(LPMEASUREITEMSTRUCT lpMIS); virtual void DrawItem(LPDRAWITEMSTRUCT lpDIS);
两个函数完全不依赖于CMenuEx类的数据成员。
要在工程中使用CMenuEx很简单:
效果如下:
主菜单
弹出式菜单
最后,对《一种漂亮的自绘菜单》的作者郑恒给予我的帮助表示衷心感谢!
一种漂亮的自绘菜单
作者:郑恒 (lbird)
下载本文示例工程
以前还是菜鸟时就觉得QQ的菜单做得很漂亮,想着自已的程序如果有那种菜单多好。
现在积累了一定的知识,就自已设计了一个类似的菜单控件类。并把它发表出来供大家使用和参考,难免有不足的地方请高手不吝赐教!
菜单效果如下:
一、CMenuEx菜单类主要接口函数:
1、void InitMenu(CMenu *pMenu,UINT uToolBar,CToolBar *pToolBar);
说明:这是最主要的一个接口。如果要改变主窗口的菜单则应在主窗口的OnInitMenu(CMenu *pMenu)中调用该函数(如:m_menu.InitMenu(pMenu,IDR_MAINFRAME,&m_wndToolBar)),当然主窗口必须要有工具栏,才会产生菜单项位图。最后还必须重载主窗口的OnMeasureItem()和OnDrawItem()。并在两个函数中分别
调用菜单类的另外两个接口DrawItem()和MeasureItem();
2、void SetHighLightColor(COLORREF crColor);
3、void SetBackColor(COLORREF);
4、void SetTextColor(COLORREF);
以上三个接口应该不用再说明 。
QQ的菜单采用的颜色是
SELECTTEXT_COLOR RGB(0,0,127)
TEXT_COLOR RGB(0,0,0)
BK_COLOR RGB(143,167,207) 效果不错
5、void SetImageLeft(UINT idBmpLeft)
说明:这也是个重要的接口。要实现纵向位图(像“开始”菜单)。可以在适当的地方调用该函数。参数当然是位图的ID值。注意选择位图时要选好看一点的哦!要记得高宽的比例。
6、void InitPopupMenu(CMenu *pMenu,UINT uToolBar,CToolBar *pToolBar)
与InitMenu不同的是:InitMenu并不修改第一级菜单为自绘风格,而
该函数有包括第一级菜单.
但必须注意:该类的任一实例都只能调用这两个函数中的一个,不能一同使用!!!
CMenuEx m_menu2、CMainFrame类添加WM_INITMENU消息,并在实现函数中加入代码:
m_menu.InitMenu(pMenu,IDR_MAINFRAME,&m_wndToolBar);IDR_MAINFRAME是工具栏资源ID,m_wndToolBar是工具栏对象
if(!nIDCtl) m_menu.DrawItem(lpDrawItemStruct); if(!nIDCtl) m_menu.MeasureItem(lpMeasureItemStruct);只须这三步就大功造成了!
m_menu.SetImageLeft(IDB_BITMAP1);//IDB_BITMAP1是指定位图2.2 用CMenuEx类加载弹出菜单的具体步骤:
CMainFrame *pFrame=(CMainFrame *)m_pMainWnd; CChildView *pView=(CChildView *)pFrame->GetActiveView(); pView->m_pToolBar=&(pFrame->m_wndToolBar);//注意:原m_wndToolBar是私有变量,要改成public的哦!当CView派生类是CMainFrame类的成员变量时,就很简单,直接在OnCreate()中设置
m_menu.CreatePopupMenu(); m_menu.AppendMenu(0,ID_EDIT_UNDO,"撤消"); m_menu.AppendMenu(MF_SEPARATOR,0); m_menu.AppendMenu(0,ID_EDIT_COPY,"复制"); m_menu.AppendMenu(0,ID_EDIT_CUT,"剪切"); m_menu.AppendMenu(0,ID_EDIT_PASTE,"粘贴");//当菜单ID与工具栏按钮ID一样时就会显示位图4、在CChildView的析构函数中加入代码:
m_menu.DestroyMenu(); //释放资源5、在CChildView类中加入WM_INITMENUPOPUP消息,并在其实现函数中加入代码:
if(!bSysMenu) m_menu.InitPopupMenu(pPopupMenu,IDR_MAINFRAME,m_pToolBar);6、在CChildView类中加入WM_CONTEXTMENU消息,并在其实现函数中加入弹出菜单的代码:
m_menu.TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON,point.x,point.y,this);好了!介绍完了。
m_menu.SetHighLightColor(RGB(0,0,127)); m_menu.SetTextColor(RGB(0,0,0)); m_menu.SetBackColor(RGB(143,167,207));