Visual C++为开发人员提供了应用程序自动生成方法,使开发人员可以避开繁琐的界面开发,专著于各种功能的具体实现。虽然这种自动生成应用程序框架的手段方便了初级程序设计人员,但是它也有着先天的不足,那就是通过这种手段生成的应用程序界面比较简单,不适宜应用程序的推广。对于Visual C++自动生成的框架程序,要实现菜单的上述功能,唯一的办法就是通过菜单自绘的手段来实现,本例将通过实现CMenu类的子类CmenuEx类,详细介绍了如何编程实现菜单的自绘,使应用程序中的菜单也可以拥有图标,并且能够反映用户的不同操作,下图为程序编译运行后的效果:
图一、自绘菜单实现后的效果图 |
一、实现方法:
要实现漂亮的界面菜单,必须要启动菜单项的自绘功能,所谓菜单的自绘,就是让菜单自己管理自己的显示效果,为此,首先要作的就是设置菜单项的风格为MF_OWNERDRAW(自绘制),设置菜单的自绘功能即可以通过CMenu类的AppendMenu()函数在菜单的初始阶段实现,也可以通过ModifyMenu()函数对已存在的菜单项进行类型修改。
具体的菜单的自绘是通过重载CMenu类的DrawItem()函数来实现的,这个函数根据各种菜单状态,处理当前菜单项中菜单图标、文字显示的功能。DrawItem()函数的原形为:virtual void DrawItem( LPDRAWITEMSTRUCT lpDrawItemStruct ),它参数为一个指向DRAWITEMSTRUCT结构的指针,DRAWITEMSTRUCT结构如下:
typedef struct tagDRAWITEMSTRUCT { UINT CtlType; //控件类型; UINT CtlID; //组合框、列表框等控件的ID标识号; UINT itemID; //菜单项的ID标识号或列表框、组合框中某一项的索引值; UINT itemAction; //控件行为; UINT itemState; //控件状态; HWND hwndItem; //父窗口句柄或菜单句柄 HDC hDC; //控件对应的绘图设备句柄 RECT rcItem; //控件所占据的矩形区域 DWORD itemData; //列表框或组合框中某一项的值 } |
可以看出,上面的DRAWITEMSTRUCT结构包含了控件自绘时的各种信息。
其中,结构成员CtlType指定了控件的类型,其取值ODT_BUTTON表示按钮控件;ODT_COMBOBOX表示组合框控件;ODT_LISTBOX表示列表框控件;ODT_LISTVIEW表示列表视图控件;ODT_MENU菜单项;ODT_STATIC表示静态文本控件;ODT_TAB表示Tab控件。CtlID指定了自绘控件的ID值,而对于菜单项则不需要使用该成员。itemID表示菜单项ID,也可以表示列表框或者组合框中某项的索引值,对于一个空的列表框或组合框,该成员的值为-1。
itemAction指定绘制行为,其取值可以为下表中所示值的一个或者多个的联合:ODA_DRAWENTIRE表示整个控件都需要被绘制;ODA_FOCUS表示控件需要在获得或失去焦点时被绘制;ODA_SELECT表示控件需要在选中状态改变时被绘制。
itemState指定了当前绘制操作时所绘项的状态,例如,如果菜单项应该被灰色显示,则可以指定ODS_GRAYED状态标志。其取值可以为下表中所示值的一个或者多个的联合:ODS_CHECKED表示菜单项将被选中,该值只对菜单项有用;
ODS_COMBOBOXEDIT在自绘组合框控件中只绘制选择区域;
ODS_DEFAULT表示当前控件处于默认状态;
ODS_DISABLED表示控件将被禁止;
ODS_FOCUS表示控件需要输入焦点;
ODS_GRAYED表示控件需要被灰色显示,该值只在绘制菜单时使用;
ODS_HOTLIGHT表示鼠标指针位于控件之上时控件会显示高亮颜色(支持 Windows 98/Me, Windows 2000/XP);
ODS_SELECTED表示选中控件;hwndItem 指定了组合框、列表框和按钮等自绘控件的窗口句柄;如果自绘的对象时菜单项,则表示包含该菜单项的菜单句柄。hDC指定了绘制操作所使用的设备环境。 rcItem指定了将被绘制的矩形区域。这个矩形区域就是上面hDC的作用范围。系统会自动裁剪组合框、列表框或按钮等控件的自绘制区域以外的部分。也就是说rcItem中的坐标点(0,0)指的就是控件的左上角。但是系统不裁剪菜单项,所以在绘制菜单项的时候,必须先通过一定的换算得到该菜单项的位置,以保证绘制操作在我们希望的区域中进行。
itemData这个成员变量最为关键,菜单自绘时所需要的图标、文本等信息都是通过它获取的,至于它的具体值,是通过CMenu类的CMenu::AppendMenu()、CMenu::InSertMenu()、CMenu::ModifMenu()等函数的调用来传递的。
菜单自绘仅仅重载CMenu::DrawItem()函数是不够的,还需要重载CMenu:: MeasureItem()函数,在这个函数里面填充MEASUREITEMSTRUCT结构,通知Windows自绘控件的尺寸。该函数的原形为:
virtual void MeasureItem( LPMEASUREITEMSTRUCT lpMeasureItemStruct ); |
该函数的参数为一个指向MEASUREITEMSTRUCT结构的指针对象,该对象结构为:
typedef struct tagMEASUREITEMSTRUCT { UINT CtlType; //控件类型; UINT CtlID; //控件的ID识别号,它不包括菜单控件; UINT itemID; //菜单项的ID识别号; UINT itemWidth; //菜单项的宽度; UINT itemHeight; //菜单项的高度; DWORD itemData //自绘控件所需要的数据, } MEASUREITEMSTRUCT; |
上面这个结构中,成员变量CtlType等于ODT_COMBOBOX 时,表示当前控件为自绘型的组合框,等于ODT_LISTBOX 时表示当前控件为自绘列表控制件,等于ODT_MENU 时表示当前控件为自绘菜单。对于组合框和列表框控件,成员变量itemData是通过相应的AddString()、InsertString()获取的, 对于菜单控件,成员变量itemData与DRAWITEMSTRUCT结构中的itemData是一致的。
菜单自绘时所需要的图标资源,可以预先定义,使用时直接装载,但是这种方法比较呆板,另外一种方法是通过搜索状态条上的按钮信息,如果当前按钮的ID识别号与某一菜单项的ID识别号一致,那末通过就将该按钮上的图标提取出来,作为菜单图标,如果菜单项与工具条上的所有按钮的ID都不相同,那么对该菜单项不画图标。至于菜单的文本信息,直接采用用户自定义菜单的文本就可以了。
根据上面介绍的知识,本例定义一个CMemu类的子类CMemuEx类来实现菜单的自绘功能,该类不仅支持在菜单中显示图标、即时反映当前菜单项状态的功能,还支持在菜单中添加纵向位图。在具体实现过程中,CMemuEx类除了上述介绍的需要重载的DrawItem()、MeasureItem()等函数外,另外的一些主要成员函数如下:
1、void InitMenu(CMenu *pMenu,UINT uToolBar,CToolBar *pToolBar);
说明:这是CMenuEx类最主要的一个接口。该函数根据状态栏信息来初始化CMenuEx类;
2、void SetImageLeft(UINT idBmpLeft);
说明:这也是CMenuEx类中的一个重要的接口。调用CMenuEx类对象的SetImageLeft()可以实现菜单中的纵向位图(像Windows系统中的"开始"菜单),调用该函数时参数是位图的ID值。需要注意的是,目前CMenuEx类实现的是对主框架菜单设置纵向位图,对上下文菜单不适用,读者朋友可以稍加修改,*的决定对何种菜单设置纵向位图。
3、void InitPopupMenu(CMenu *pMenu,UINT uToolBar,CToolBar *pToolBar)
说明:这个函数是为了处理上下文菜单的自绘而编写的,CMenuEx类的任一实例都只能调用InitMenu()、InitPopupMenu()这两个成员函数中的一个,不能一同使用。
4、int GetImageFromToolBar(UINT uToolBar, CToolBar *pToolBar,COLORREF crMask)
说明:这个函数用来从工具条上获取相应图标。
5、void ChangeStyle(CMenu *pMenu,CToolBar *pToolBar,BOOL bIsMainMenu)
说明:这个函数用来修改菜单pMenu的类型为"自绘制"
6、void DrawMenuItemImage(CDC *pDC,CRect &rect,BOOL bSelected,BOOL bChecked,BOOL bGrayed,BOOL bHasImage,LPMENUITEM lpItem);
说明:这个函数根据菜单的不同状态及包含的各种信息,对菜单进行绘制;
上述函数构成了CMenuEx类的主要骨架,该类还有一些实现辅助函数,它们分别是:
1、void SetHighLightColor(COLORREF crColor); //设置菜单搞亮显示;
2、void SetBackColor(COLORREF); //设置菜单的背景颜色;
3、void SetTextColor(COLORREF); //设置菜单的文本颜色;
4、void GrayString(CDC *pDC,const CString &str,const CRect rect); //显示灰色菜单文本;
5、void TextMenu(CDC *pDC,CRect &rect,CRect rtText,BOOL bSelected,BOOL bGrayed,LPMENUITEM lpItem); //显示菜单上的文本;
CMenuEx类在使用过程中,要注意对其初始化,对于主框架菜单,可以在CMainFrame类的WM_INITMENU消息响应函数中实现,为了激活菜单的自绘功能, 需要在CMainFrame类的WM_DRAWITEM和WM_MEASUREITEM中分别调用CMenuEx类的DrawItem()函数和MeasureItem()函数。对于上下文菜单的实现,有两种方法,一种方法是在项目视图类的WM_INITMENUPOPUP响应函数中调用InitPopupMenu()函数来修改菜单的类型,然后在WM_CONTEXTMENU响应函数中调用CMenu::TrackPopupMenu()函数显示上下文菜单;另一种方法是直接响应鼠标的右键单击消息,在响应函数中处理鼠标的初始化和显示。第二种方法处理上下文菜单比较常用,这方面的资料也很多,就不在赘述了。为了让读者朋友们更深入的了解上下文菜单的处理,例子中使用了第一种方法。
二、编程步骤
1、 启动Visual C++6.0生成一个单文档应用程序框架,去除文档支持,将应用程序命名为Hello;
2、 在CMainFrame类中添加CMenuEx类的成员变量m_menu;
3、 使用Class Wizard在CMainFrame类添加WM_INITMENU、WM_DRAWITEM、WM_MEASUREITEM消息响应函数、在ChildView类中添加WM_INITMENUPOPUP、WM_CONTEXTMENU消息响应函数;
4、 将需要显示图标的菜单的ID识别号与工具条上响应的按钮的ID识别号统一起来;
5、 在CChildView类中添加成员变量CToolBar * m_pToolBar和 CMenuEx m_menu。m_pToolBar对象主要是用来存放程序中的工具条,从而提供给上下文菜单m_menu自画时所需要的图标信息。
6、添加代码,编译运行程序。
三、程序代码
//////////////////////////////////////////////CMenuEx类的文件 #if !defined(AFX_MENUEX_H__FE677F6B_2315_11D7_8869_BB2B2A4F4D45__INCLUDED_) #define AFX_MENUEX_H__FE677F6B_2315_11D7_8869_BB2B2A4F4D45__INCLUDED_ #include "afxtempl.h" #if _MSC_VER > 1000 #pragma once #endif // _MSC_VER > 1000 typedef struct tagMENUITEM //该结构用存放菜单自绘时所需要的信息; { CString strText; //菜单项的文本; UINT uID; //菜单的ID标识号; UINT uIndex; //菜单项所要画的图标在工具条图标序列中的索引号; int uPositionImageLeft; //当前菜单项在纵向菜单中的位置; }MENUITEM; typedef MENUITEM * LPMENUITEM; ////////////////////////////////////////////////////////////// class CMenuEx : public CMenu { public: void InitPopupMenu(CMenu *pPopupMenu,UINT uToolBar,CToolBar *pToolBar); void ChangeStyle(CMenu *pMenu,CToolBar *pToolBar,BOOL bIsMainMenu=FALSE); void SetHighLightColor(COLORREF crColor); void SetBackColor(COLORREF); void SetTextColor(COLORREF); void SetImageLeft(UINT idBmpLeft); void MeasureItem(LPMEASUREITEMSTRUCT lpMIS); void InitMenu(CMenu *pMenu,UINT uToolBar,CToolBar *pToolBar); void DrawItem(LPDRAWITEMSTRUCT lpDIS); CMenuEx(); virtual ~CMenuEx(); protected: int m_nSeparator; CSize m_szImageLeft; //纵向位图尺寸; CBitmap m_bmpImageLeft; //纵向位图对象; int m_nWidthLeftImage; //纵向位图的宽度; BOOL m_bHasImageLeft; //是否有纵向位图 BOOL m_bInitial; //菜单是否已初始化,即设定了自绘风格 int GetImageFromToolBar(UINT uToolBar,CToolBar *pToolBar,COLORREF crMask=RGB(192,192,192)); //从工具条中获取图标信息; CList<MENUITEM *,MENUITEM *> m_ListMenu; COLORREF m_colMenu; COLORREF m_colTextSelected; void DrawImageLeft(CDC *pDC,CRect &rect,LPMENUITEM lpItem); //画纵向位图; void TextMenu(CDC *pDC,CRect &rect,CRect rtText,BOOL bSelected,BOOL bGrayed,LPMENUITEM lpItem); //画菜单文本; CImageList m_ImageList; //图像列表; COLORREF m_colText; CSize m_szImage; //菜单项位图的大小 void DrawMenuItemImage(CDC *pDC,CRect &rect,BOOL bSelected,BOOL bChecked,BOOL bGrayed,BOOL bHasImage,LPMENUITEM lpItem);//画菜单图标; void GrayString(CDC *pDC,const CString &str,const CRect rect);//显示灰色字符串; }; #endif /////////////////////////////////////////////////CMenuEx类的实现文件; #include "stdafx.h" #include "MenuEx.h" #ifdef _DEBUG #undef THIS_FILE static char THIS_FILE[]=__FILE__; #define new DEBUG_NEW #endif ///////////////////////////////////////////// //构造函数; CMenuEx::CMenuEx():m_szImage(16,15) { m_colMenu =::GetSysColor(COLOR_MENU); m_colText =::GetSysColor(COLOR_MENUTEXT); m_colTextSelected =::GetSysColor(COLOR_HIGHLIGHTTEXT); m_bInitial =FALSE; m_bHasImageLeft =FALSE; m_nSeparator = 10; //菜单分割条的默认高度 } //////////////////////////////////////////////析构函数; CMenuEx::~CMenuEx() { m_ImageList.DeleteImageList(); //清空图像列表; while(!m_ListMenu.IsEmpty()) //清空菜单项信息列表; delete m_ListMenu.RemoveHead(); if(m_bHasImageLeft) //释放纵向位图对象; m_bmpImageLeft.DeleteObject(); } ///////////////////////////////////////////////////当菜单项为不可用时绘制灰色的文本; void CMenuEx::GrayString(CDC *pDC, const CString &str, const CRect rect) { CRect rt(rect); rt.left +=1; rt.top +=1; pDC->SetTextColor(RGB(255,255,255)); pDC->DrawText(str,&rt,DT_EXPANDTABS|DT_VCENTER|DT_SINGLELINE); rt.left -=1; rt.top -=1; pDC->SetTextColor(RGB(127,127,127)); pDC->DrawText(str,&rt,DT_EXPANDTABS|DT_VCENTER|DT_SINGLELINE); } ///////////////////////////////////////////////////绘制菜单项位图 void CMenuEx::DrawMenuItemImage(CDC *pDC, CRect &rect, BOOL bSelected, BOOL bChecked, BOOL bGrayed, BOOL bHasImage,LPMENUITEM lpItem) { CRect rt(rect.left ,rect.top ,rect.left + m_szImage.cx + 4, rect.top + m_szImage.cy + 4); //确定显示图标的位置; if(bChecked) //根据不同的状态画菜单的选择标志; { if(bGrayed) { //菜单不可用 GrayString(pDC,"√",rt); } else { if(bSelected) { //当该菜单项被选中时绘制一个立体矩形 pDC->Draw3dRect(&rt,RGB(255,255,255),RGB(127,127,127)); } rt.InflateRect(-2,-2); //画出"√" pDC->SetBkMode(TRANSPARENT); pDC->SetTextColor(m_colText); pDC->DrawText("√",&rt,DT_EXPANDTABS|DT_VCENTER|DT_SINGLELINE); } rect.left +=m_szImage.cx + 4 +2 ; //重新计算rect的尺寸,为显示菜单文本作准备; return ; } if(bHasImage) //如果菜单有图标 { CPoint pt(rt.left+2 , rt.top+2 ); UINT uStyle =ILD_TRANSPARENT; //CImageList::Draw()绘制位图的风格 if(bGrayed) { uStyle |=ILD_BLEND50; //菜单不可用所以位图较暗 } else { if(bSelected) { //当该项被选中仅多绘制一个立体矩形 pDC->Draw3dRect(&rt,RGB(255,255,255),RGB(127,127,127)); } } m_ImageList.Draw(pDC,lpItem->uIndex,pt,uStyle); //在菜单项中绘制图标; //调整可绘制矩形的大小,位图外接矩形比位图大4,菜单文本与位图外接矩形的间隔为2 rect.left +=m_szImage.cx + 4 + 2; } } ///////////////////////////////////////////////////绘制菜单项文本 void CMenuEx::TextMenu(CDC *pDC, CRect &rect,CRect rtText,BOOL bSelected, BOOL bGrayed, LPMENUITEM lpItem) { //选中状态的菜单项要先画出立体矩形 if(bSelected) pDC->Draw3dRect(&rect,RGB(127,127,127),RGB(255,255,255)); if(bGrayed) { GrayString(pDC,lpItem->strText,rtText); } else { pDC->DrawText(lpItem->strText,rtText,DT_LEFT|DT_EXPANDTABS|DT_VCENTER); } } ///////////////////////////////////////////////////画菜单的纵向位图; void CMenuEx::DrawImageLeft(CDC *pDC, CRect &rect,LPMENUITEM lpItem) { if(!m_bHasImageLeft || lpItem->uPositionImageLeft ==-1) return ; CDC memDC; memDC.CreateCompatibleDC(pDC); //装载位图; CBitmap *oldBmp =(CBitmap *) memDC.SelectObject(&m_bmpImageLeft); int cy; //设定该菜单项应从哪画起 if(m_szImageLeft.cy >= lpItem->uPositionImageLeft + rect.Height()) { cy =(int) m_szImageLeft.cy - lpItem->uPositionImageLeft - rect.Height(); ASSERT(cy>=0); } else cy =0; //画图; pDC->BitBlt(rect.left ,rect.top ,m_szImageLeft.cx ,rect.Height(),&memDC,0,cy,SRCCOPY); memDC.SelectObject(oldBmp); memDC.DeleteDC(); rect.left +=m_szImageLeft.cx+1; } /////////////////////////////////////////////////////////////菜单自画; void CMenuEx::DrawItem(LPDRAWITEMSTRUCT lpDIS) { CDC dc; LPMENUITEM lpItem; CRect rect(lpDIS->rcItem); dc.Attach(lpDIS->hDC); //得到自画菜单所需要的信息; lpItem =(LPMENUITEM)lpDIS->itemData; //根据不同的状态设置菜单的文本颜色; if(lpDIS->itemState & ODS_SELECTED) dc.SetTextColor(m_colTextSelected); else dc.SetTextColor(m_colText); //设定背景色 CBrush brush(m_colMenu); dc.FillRect(&rect, &brush); //设定显示模式 dc.SetBkMode(TRANSPARENT); //绘制侧边位图 DrawImageLeft(&dc,rect,lpItem); if(lpItem->uID==0)//分隔条 { rect.top =rect.Height()/2+rect.top ; rect.bottom =rect.top +2; rect.left +=2; rect.right -=2; dc.Draw3dRect(rect,RGB(64,0,128),RGB(255,255,255)); } else { //得到当前菜单项目的状态; BOOL bSelected =lpDIS->itemState & ODS_SELECTED; BOOL bChecked =lpDIS->itemState & ODS_CHECKED; BOOL bGrayed =lpDIS->itemState & ODS_GRAYED; BOOL bHasImage =(lpItem->uIndex!=-1); //设定菜单文本的区域 CRect rtText(rect.left+m_szImage.cx+4+2, rect.top,rect.right ,rect.bottom ); rtText.InflateRect(-2,-2); //绘制菜单位图 DrawMenuItemImage(&dc,rect,bSelected,bChecked,bGrayed,bHasImage,lpItem); //绘制菜单文本 TextMenu(&dc,rect,rtText,bSelected,bGrayed,lpItem); } dc.Detach(); } ////////////////////////////////////////////////////////////改变菜单风格 //注意第二个参数:FALSE:表示pMenu指向的不是主框架菜单 void CMenuEx::ChangeStyle(CMenu *pMenu,CToolBar *pToolBar,BOOL bIsMainMenu) { ASSERT(pMenu); TRACE("ChangeStyle/n"); LPMENUITEM lpItem; CMenu *pSubMenu; int m,nPosition=0; //该变量用来绘制纵向位图的位置 int inx; UINT idx,x; for(int i=(int)pMenu->GetMenuItemCount()-1 ;i>=0; i--) { //得到菜单的信息 lpItem =new MENUITEM; lpItem->uID =pMenu->GetMenuItemID(i); if(!bIsMainMenu) //不是第一级菜单 lpItem->uPositionImageLeft =-1;//上下文菜单不支持纵向位图 else lpItem->uPositionImageLeft =nPosition; if(lpItem->uID >0) { if(bIsMainMenu) nPosition +=m_szImage.cy+4; //保存菜单文本 pMenu->GetMenuString(i,lpItem->strText,MF_BYPOSITION); //由工具栏位图中寻找菜单项的位图,如果没有则uIndex为-1 lpItem->uIndex =-1; if(pToolBar) { for(m=0; m<(pToolBar->GetToolBarCtrl().GetButtonCount()) ;m++) { pToolBar->GetButtonInfo(m,idx,x,inx); if(idx==lpItem->uID) { lpItem->uIndex=inx; break; } } } //如果该项下还有子菜单,则递归调用该函数来修改其子菜单的风格 pSubMenu =pMenu->GetSubMenu(i); if(pSubMenu) ChangeStyle(pSubMenu,pToolBar); } else { if(bIsMainMenu) nPosition +=m_nSeparator; } //修改菜单风格为自绘 pMenu->ModifyMenu(i,MF_BYPOSITION|MF_OWNERDRAW,lpItem->uID,(LPCTSTR)lpItem); m_ListMenu.AddTail(lpItem); } } /////////////////////////////////////////////由工具栏的位图来产生菜单所用的位图列表m_ImageList; int CMenuEx::GetImageFromToolBar(UINT uToolBar, CToolBar *pToolBar,COLORREF crMask) { if(!pToolBar) return 0; CBitmap bmp; int nWidth,nHeight; BITMAP bmpInfo; bmp.LoadBitmap(uToolBar);//将工具条作为位图资源装载; bmp.GetBitmap(&bmpInfo); //得到位图的高度 nHeight =bmpInfo.bmHeight; int nCount=0; int ret =pToolBar->GetToolBarCtrl().GetButtonCount(); //得到工具栏中位图的个数nCount for(int i=0;i<ret;i++) if(pToolBar->GetItemID(i)!=ID_SEPARATOR) nCount ++; //计算出位图的宽度 nWidth =bmpInfo.bmWidth/nCount; bmp.DeleteObject(); TRACE("Menu Bitmap--width:%d/theight:%d/n",nWidth,nHeight); //创建位图列表 m_ImageList.Create(uToolBar,nWidth,nHeight,crMask); m_szImage.cx =nWidth; m_szImage.cy =nHeight; return nCount; } //////////////////////////////////////////////////////////////////初始化菜单对象; void CMenuEx::InitMenu(CMenu *pMenu, UINT uToolBar, CToolBar *pToolBar) { //已设定了风格 if(m_bInitial) return ; GetImageFromToolBar(uToolBar,pToolBar); CMenu *pSubMenu,*pSubsub; MENUITEM *lpItem; UINT i; int j,m; int nPosition; //该变量用来存放纵向位图的位置 for(i=0;i<pMenu->GetMenuItemCount();i++) { pSubMenu =pMenu->GetSubMenu(i); if(pSubMenu) { nPosition =0; //注意j一定要为int类型,如果为UINT是检查不出j>=0! for(j=(int)pSubMenu->GetMenuItemCount()-1;j>=0;j--) { lpItem =new MENUITEM; lpItem->uID =pSubMenu->GetMenuItemID(j); lpItem->uPositionImageLeft =nPosition; if(lpItem->uID>0) { nPosition +=m_szImage.cy+4; pSubMenu->GetMenuString(j,lpItem->strText,MF_BYPOSITION); //由工具栏位图中寻找菜单项的位图,如果没有则uIndex为-1 lpItem->uIndex =-1; for(m=0; m<(pToolBar->GetToolBarCtrl().GetButtonCount()) ;m++) { int inx; UINT idx,x; pToolBar->GetButtonInfo(m,idx,x,inx); if(idx==lpItem->uID) { lpItem->uIndex=inx; break; } } } else { //间隔条; nPosition +=m_nSeparator; } m_ListMenu.AddTail(lpItem); //修改菜单绘制风格; pSubMenu->ModifyMenu(j,MF_BYPOSITION|MF_OWNERDRAW, lpItem->uID,LPCTSTR(lpItem)); pSubsub =pSubMenu->GetSubMenu(j); if(pSubsub) ChangeStyle(pSubsub,pToolBar); } } } m_bInitial =TRUE; } //////////////////////////////////////////////////////重载CMenu类的MeasureItem()函数; void CMenuEx::MeasureItem(LPMEASUREITEMSTRUCT lpMIS) { MENUITEM *lpItem =(LPMENUITEM)lpMIS->itemData; if(lpItem->uID==0)//分隔条的高度为10个像素; { lpMIS->itemHeight =m_nSeparator; } else { //填充lpMIS结构; CDC *pDC =AfxGetMainWnd()->GetDC(); CString strText=lpItem->strText; CSize size; size=pDC->GetTextExtent(lpItem->strText); lpMIS->itemWidth = size.cx +m_szImage.cx+4; lpMIS->itemHeight =m_szImage.cy+4; AfxGetMainWnd()->ReleaseDC(pDC); } } ////////////////////////////////////////////////////////设置菜单上的纵向位图; void CMenuEx::SetImageLeft(UINT idBmpLeft) { m_bmpImageLeft.LoadBitmap(idBmpLeft);//装载纵向位图; m_bHasImageLeft = TRUE; BITMAP bmpInfo; m_bmpImageLeft.GetBitmap(&bmpInfo); m_szImageLeft.cx =bmpInfo.bmWidth; m_szImageLeft.cy =bmpInfo.bmHeight; } ////////////////////////////////////////////////////////////////设置菜单上的文本颜色; void CMenuEx::SetTextColor(COLORREF crColor) { m_colText =crColor; } ////////////////////////////////////////////////////////////设置菜单的背景颜色; void CMenuEx::SetBackColor(COLORREF crColor) { m_colMenu =crColor; } //////////////////////////////////////////////////////////设置菜单高亮显示时的颜色; void CMenuEx::SetHighLightColor(COLORREF crColor) { m_colTextSelected =crColor; } //////////////////////////////////////////////////////初始化上下文菜单; void CMenuEx::InitPopupMenu(CMenu *pPopupMenu,UINT uToolBar, CToolBar *pToolBar) { if(m_bInitial) return ; GetImageFromToolBar(uToolBar,pToolBar); ChangeStyle(pPopupMenu,pToolBar); m_bInitial =TRUE; } ////////////////////////////////////////////////////在程序中使用CMenuEx类对象实现菜单的自画; int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) { …………………………….. if (!m_wndStatusBar.Create(this) ||!m_wndStatusBar.SetIndicators(indicators, sizeof(indicators)/sizeof(UINT))) { TRACE0("Failed to create status bar/n"); return -1; // fail to create } m_wndToolBar.EnableDocking(CBRS_ALIGN_ANY); EnableDocking(CBRS_ALIGN_ANY); DockControlBar(&m_wndToolBar); m_wndView.m_pToolBar =&m_wndToolBar; // 将视图内的工具条变量附值 return 0; } ///////////////////////////////////////////////////////////////初始自画菜单; void CMainFrame::OnInitMenu(CMenu* pMenu) { CFrameWnd::OnInitMenu(pMenu); // TODO: Add your message handler code here m_menu.InitMenu(pMenu,IDR_MAINFRAME,&m_wndToolBar); } ////////////////////////////////////////////////////////////////////通知菜单自画; void CMainFrame::OnDrawItem(int nIDCtl, LPDRAWITEMSTRUCT lpDrawItemStruct) { // TODO: Add your message handler code here and/or call default if(!nIDCtl) m_menu.DrawItem(lpDrawItemStruct); CFrameWnd::OnDrawItem(nIDCtl, lpDrawItemStruct); } //////////////////////////////////////////////////////////////////通知菜单确定尺寸; void CMainFrame::OnMeasureItem(int nIDCtl, LPMEASUREITEMSTRUCT lpMeasureItemStruct) { // TODO: Add your message handler code here and/or call default if(!nIDCtl) m_menu.MeasureItem(lpMeasureItemStruct); CFrameWnd::OnMeasureItem(nIDCtl, lpMeasureItemStruct); } /////////////////////////////////////////////////////////////////初始化上下文菜单; CChildView::CChildView() { 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,_T("粘贴")); } /////////////////////////////////////////////////////////////////////////////// CChildView::~CChildView() //销毁上下文菜单; { m_menu.DestroyMenu(); } ///////////////////////////////////////////////////////////////////修改菜单实现自画; void CChildView::OnInitMenuPopup(CMenu* pPopupMenu, UINT nIndex, BOOL bSysMenu) { CWnd ::OnInitMenuPopup(pPopupMenu, nIndex, bSysMenu); // TODO: Add your message handler code here if(!bSysMenu) m_menu.InitPopupMenu(pPopupMenu,IDR_MAINFRAME,m_pToolBar); } /////////////////////////////////////////////////////////////显示上下文菜单; void CChildView::OnContextMenu(CWnd* pWnd, CPoint point) { // TODO: Add your message handler code here m_menu.TrackPopupMenu(TPM_LEFTALIGN|TPM_RIGHTBUTTON,point.x,point.y,this); } |
四、小结
到此为止,本例详细介绍了菜单自绘类CMenuEx的实现以及它在应用程序的具体使用方法,相信读者能够从中学习到菜单的自绘制机理。本例中的CMenuEx类稍加改动,就可以实现各种不同类型的菜单效果,如 WindowsXP风格的菜单等。