Visual C++中自绘菜单的实现

 

  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 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 "); 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  if(pToolBar->GetItemID(i)!=ID_SEPARATOR)
  nCount ++;
 //计算出位图的宽度
 nWidth =bmpInfo.bmWidth/nCount;
 bmp.DeleteObject();
 TRACE("Menu Bitmap--width:%d height:%d ",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;iGetMenuItemCount();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 ");
  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风格的菜单等。

 

从VC++项目中的菜单资源建立结构相同的自绘弹出式菜单,原理和步骤如下: (1)CMenu::LoadMenu读入菜单资源; (2)CImageList::Create读入工具栏位图; (3)CMenu::CreatePopupMenu和CMenu::AppendMenu拷贝菜单资源,建立弹出式菜单。其中CMenu::AppendMenu第1个参数设置成MF_OWNERDRAW(自绘), 第四个参数设置成一个附加结构的指针,包括菜单项文字和位图索引等信息。通过这个结构,在自绘制时,可以获取对应的菜单项文字和位图位置索引,其中位图保存在第(2)步中的CImageList变量中; (4)在对右鼠标键的响应函数里,使用CMenu::TrackPopupMenu启动显示弹出式菜单; (5)在弹出式菜单的拥有者窗口(CxxxView)里,处理WM_MEASUREITEM消息和WM_DRAWITEM消息,分别调用CMenuEx::MeasureItem和CMenuEx::DrawItem, 分别用来定义菜单项的尺寸,对菜单项进行自绘; (6)在自绘函数CMenuEx::DrawItem里,通过每个菜单项的附加结构lpDIS->itemData,获得其文字和位图索引,然后分别使用CDC::DrawText和CImageList::Draw,画出该菜单项的文字和位图,从而实现自绘制。 程序在VC6下编译通过。 没有处理的地方:如果菜单项状态是checked或者radio,程序没做处理。另外,弹出式菜单的激活/禁止时,不会自动触发其拥有者窗口的ON_UPDATE_COMMAND_UI宏。不过,可以处理owner窗口的WM_INITMEMUPOPUP消息(在弹出式菜单的每个子菜单弹出时,都会发出此消息),为每个子菜单项单独生成一个CCmdUI对象,调用其CCmdUI::DoUpdate函数,来手动触发ON_UPDATE_COMMAND_UI宏中对应的消息处理函数,使得菜单项能够根据应用环境进行激活和禁止。详见博客: http://oliver.zheng.blog.163.com/blog/static/14241159520143210595266/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值