菜单自绘方面的知识 收藏
1要实现漂亮的界面菜单,必须要启动菜单项的自绘功能,所谓菜单的自绘,就是让菜单自己管理自己的显示效果,为此,首先要作的就是设置菜单项的风格为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中的坐标点(00)指的就是控件的左上角。但是系统不裁剪菜单项,所以在绘制菜单项的时候,必须先通过一定的换算得到该菜单项的位置,以保证绘制操作在我们希望的区域中进行。

  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()获取的, 对于菜单控件,成员变量itemDataDRAWITEMSTRUCT结构中的itemData是一致的。

  菜单自绘时所需要的图标资源,可以预先定义,使用时直接装载,但是这种方法比较呆板,另外一种方法是通过搜索状态条上的按钮信息,如果当前按钮的ID识别号与某一菜单项的ID识别号一致,那末通过就将该按钮上的图标提取出来,作为菜单图标,如果菜单项与工具条上的所有按钮的ID都不相同,那么对该菜单项不画图标。至于菜单的文本信息,直接采用用户自定义菜单的文本就可以了
--  Visual C++中自绘菜单的实现
  应用程序中的菜单,在界面中占据了重要位置,它的效果如何,直接影响了整个程序的界面效果,正因为这个原因,当今流行的应用程序的菜单都支持附带图标、反映当前状态的功能,也就是说,菜单项上不再仅仅有文字,还有附带一个小小的图标,同时在用户操作菜单时,菜单能够以不同的状态反映用户的操作,这些功能的实现,可以大大,美化程序界面,增强程序的吸引力。

  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中的坐标点(00)指的就是控件的左上角。但是系统不裁剪菜单项,所以在绘制菜单项的时候,必须先通过一定的换算得到该菜单项的位置,以保证绘制操作在我们希望的区域中进行。

  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()获取的, 对于菜单控件,成员变量itemDataDRAWITEMSTRUCT结构中的itemData是一致的。

  菜单自绘时所需要的图标资源,可以预先定义,使用时直接装载,但是这种方法比较呆板,另外一种方法是通过搜索状态条上的按钮信息,如果当前按钮的ID识别号与某一菜单项的ID识别号一致,那末通过就将该按钮上的图标提取出来,作为菜单图标,如果菜单项与工具条上的所有按钮的ID都不相同,那么对该菜单项不画图标。至于菜单的文本信息,直接采用用户自定义菜单的文本就可以了。

  根据上面介绍的知识,本例定义一个CMemu类的子类CMemuEx类来实现菜单的自绘功能,该类不仅支持在菜单中显示图标、即时反映当前菜单项状态的功能,还支持在菜单中添加纵向位图。在具体实现过程中,CMemuEx类除了上述介绍的需要重载的DrawItem()、MeasureItem()等函数外,另外的一些主要成员函数如下:

  1void InitMenu(CMenu *pMenu,UINT uToolBar,CToolBar *pToolBar);

  说明:这是CMenuEx类最主要的一个接口。该函数根据状态栏信息来初始化CMenuEx类;

  2void SetImageLeft(UINT idBmpLeft)

  说明:这也是CMenuEx类中的一个重要的接口。调用CMenuEx类对象的SetImageLeft()可以实现菜单中的纵向位图(Windows系统中的"开始"菜单),调用该函数时参数是位图的ID值。需要注意的是,目前CMenuEx类实现的是对主框架菜单设置纵向位图,对上下文菜单不适用,读者朋友可以稍加修改,自由的决定对何种菜单设置纵向位图。

  3void InitPopupMenu(CMenu *pMenu,UINT uToolBar,CToolBar *pToolBar)

  说明:这个函数是为了处理上下文菜单的自绘而编写的,CMenuEx类的任一实例都只能调用InitMenu()、InitPopupMenu()这两个成员函数中的一个,不能一同使用。

  4int GetImageFromToolBar(UINT uToolBar, CToolBar *pToolBar,COLORREF crMask)

  说明:这个函数用来从工具条上获取相应图标。

  5void ChangeStyle(CMenu *pMenu,CToolBar *pToolBar,BOOL bIsMainMenu)

  说明:这个函数用来修改菜单pMenu的类型为"自绘制"

  6void DrawMenuItemImage(CDC *pDC,CRect &rect,BOOL bSelected,BOOL bChecked,BOOL bGrayed,BOOL bHasImage,LPMENUITEM lpItem);

  说明:这个函数根据菜单的不同状态及包含的各种信息,对菜单进行绘制;

  上述函数构成了CMenuEx类的主要骨架,该类还有一些实现辅助函数,它们分别是:

  1void SetHighLightColor(COLORREF crColor); //设置菜单搞亮显示;

  2void SetBackColor(COLORREF); //设置菜单的背景颜;

  3void SetTextColor(COLORREF); //设置菜单的文本颜;

  4void GrayString(CDC *pDC,const CString &str,const CRect rect); //显示灰菜单文本;

  5void TextMenu(CDC *pDC,CRect &rect,CRect rtText,BOOL bSelected,BOOL bGrayed,LPMENUITEM lpItem); //显示菜单上的文本;

  CMenuEx类在使用过程中,要注意对其初始化,对于主框架菜单,可以在CMainFrame类的WM_INITMENU消息响应函数中实现,为了激活菜单的自绘功能, 需要在CMainFrame类的WM_DRAWITEMWM_MEASUREITEM中分别调用CMenuEx类的DrawItem()函数和MeasureItem()函数。对于上下文菜单的实现,有两种方法,一种方法是在项目视图类的WM_INITMENUPOPUP响应函数中调用InitPopupMenu()函数来修改菜单的类型,然后在WM_CONTEXTMENU响应函数中调用CMenu::TrackPopupMenu()函数显示上下文菜单;另一种方法是直接响应鼠标的右键单击消息,在响应函数中处理鼠标的初始化和显示。第二种方法处理上下文菜单比较常用,这方面的资料也很多,就不在赘述了。为了让读者朋友们更深入的了解上下文菜单的处理,例子中使用了第一种方法。
--  作者:admin
--  发布时间:2005-10-17 9:20:00

-- 
  二、编程步骤

  1 启动Visual C++6.0生成一个单文档应用程序框架,去除文档支持,将应用程序命名为Hello

  2 CMainFrame类中添加CMenuEx类的成员变量m_menu

  3 使用Class WizardCMainFrame类添加WM_INITMENUWM_DRAWITEMWM_MEASUREITEM消息响应函数、在ChildView类中添加WM_INITMENUPOPUPWM_CONTEXTMENU消息响应函数;

  4 将需要显示图标的菜单的ID识别号与工具条上响应的按钮的ID识别号统一起来;

  5 CChildView类中添加成员变量CToolBar * m_pToolBar CMenuEx m_menum_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 ,p ,rect.left + + 4, p + + 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 += + 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 += + 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( >= lpItem->uPositionImageLeft + rect.Height())
  {
   cy =(int) - lpItem->uPositionImageLeft - rect.Height();
   ASSERT(cy>=0);
  }
  else
   cy =0;
   //画图;
   pDC->BitBlt(rect.left ,p , ,rect.Height(),&memDC,0,cy,SRCCOPY);
   memDC.SelectObject(oldBmp);
   memDC.DeleteDC();
   rect.left +=+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)//分隔条
   {
    p =rect.Height()/p ;
    rect.bottom =p +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++4+2, p,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 +=+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);
  =nWidth;
  =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 +=+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 = ++4;
  lpMIS->itemHeight =+4;
  AfxGetMainWnd()->ReleaseDC(pDC);
 }
}
////////////////////////////////////////////////////////设置菜单上的纵向位图;
void CMenuEx::SetImageLeft(UINT idBmpLeft)
{
 m_bmpImageLeft.LoadBitmap(idBmpLeft);//装载纵向位图;
 m_bHasImageLeft = TRUE;
 BITMAP bmpInfo;
 m_bmpImageLeft.GetBitmap(&bmpInfo);
  =bmpInfo.bmWidth;
  =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);
 // TOD Add your message handler code here
 m_menu.InitMenu(pMenu,IDR_MAINFRAME,&m_wndToolBar);
}
////////////////////////////////////////////////////////////////////通知菜单自画;
void CMainFrame::OnDrawItem(int nIDCtl, LPDRAWITEMSTRUCT lpDrawItemStruct)
{
 // TOD 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)
{
 // TOD 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);
 // TOD Add your message handler code here
 if(!bSysMenu)
  m_menu.InitPopupMenu(pPopupMenu,IDR_MAINFRAME,m_pToolBar);
}
/////////////////////////////////////////////////////////////显示上下文菜单;
void CChildView::OnContextMenu(CWnd* pWnd, CPoint point)
{
 // TOD Add your message handler code here
 m_menu.TrackPopupMenu(TPM_LEFTALIGN|TPM_RIGHTBUTTON,point.x,point.y,this);
}

  四、小结

  到此为止,本例详细介绍了菜单自绘类CMenuEx的实现以及它在应用程序的具体使用方法,相信读者能够从中学习到菜单的自绘制机理。本例中的CMenuEx类稍加改动,就可以实现各种不同类型的菜单效果,如WindowsXP风格的菜单等。
--  作者:admin
--  发布时间:2005-10-17 9:20:00

--  一种漂亮的自绘菜单
以前还是菜鸟时就觉得QQ的菜单做得很漂亮,想着自已的程序如果有那种菜单多好。
现在积累了一定的知识,就自已设计了一个类似的菜单控件类。并把它发表出来供大家使用和参考,难免有不足的地方请高手不吝赐教!
菜单效果如下:


一、CMenuEx菜单类主要接口函数:

1void InitMenu(CMenu *pMenu,UINT uToolBar,CToolBar *pToolBar);
说明:这是最主要的一个接口。如果要改变主窗口的菜单则应在主窗口的OnInitMenu(CMenu *pMenu)中调用该函数(如:m_menu.InitMenu(pMenu,IDR_MAINFRAME,&m_wndToolBar)),当然主窗口必须要有工具栏,才会产生菜单项位图。最后还必须重载主窗口的OnMeasureItem()OnDrawItem()。并在两个函数中分别
调用菜单类的另外两个接口DrawItem()MeasureItem();
2void SetHighLightColor(COLORREF crColor);
3void SetBackColor(COLORREF);
4void SetTextColor(COLORREF);
以上三个接口应该不用再说明
QQ的菜单采用的颜是
SELECTTEXT_COLOR RGB(0,0,127)
TEXT_COLOR RGB(0,0,0)
BK_COLOR RGB(143,167,207) 效果不错
5void SetImageLeft(UINT idBmpLeft)
说明:这也是个重要的接口。要实现纵向位图(开始菜单)。可以在适当的地方调用该函数。参数当然是位图的ID值。注意选择位图时要选好看一点的哦!要记得高宽的比例。
6void InitPopupMenu(CMenu *pMenu,UINT uToolBar,CToolBar *pToolBar)
InitMenu不同的是:InitMenu并不修改第一级菜单为自绘风格,而
该函数有包括第一级菜单.
但必须注意:该类的任一实例都只能调用这两个函数中的一个,不能一同使用!!!
二、CMenuEx类的具体使用步骤:2.1 CMenuEx类加载主窗口菜单的具体步骤:

1、添加CMenuEx成员变量
CMenuEx m_menu
2CMainFrame类添加WM_INITMENU消息,并在实现函数中加入代码:
m_menu.InitMenu(pMenu,IDR_MAINFRAME,&m_wndToolBar);
IDR_MAINFRAME是工具栏资源IDm_wndToolBar是工具栏对象
3CMainFrame类添加WM_DRAWITEMWM_MEASUREITEM消息,并在实现函数分加入:
if(!nIDCtl) m_menu.DrawItem(lpDrawItemStruct);if(!nIDCtl) m_menu.MeasureItem(lpMeasureItemStruct);
只须这三步就大功造成了!
但是有时你会发现菜单的位图错位了,这不是程序的错。
这是因为工具栏位图的个数与工具栏按钮个数不符,
你只需把不用的位图删掉就可以,或者添加对应的菜单项,反正使这两个数目一致就对了

4、如果你想要让你的菜单拥有纵向位图”(开始菜单),就必须在CMainFrameOnCreate()中加入:
m_menu.SetImageLeft(IDB_BITMAP1);//IDB_BITMAP1是指定位图
2.2 CMenuEx类加载弹出菜单的具体步骤:

1、在CChildView类中加入成员变量 CMenuEx m_menuCToolBar *m_pToolBar
为什么要加入m_pToolBar,这里做一下说明:
因为在CMenuEx的接口函数InitPopupMenu()中需要工具栏对象指针,而工具栏对象又偏偏是CMainFrame类的成员
所以设此指针变量指向工具栏对象

2、给CChildView类的成员m_pToolBar赋值。
CView派生类由不得CxxxxDocTemplate产生时,赋值的地方比较特殊
请在CxxxWinApp类中的InitInstance()的最后加入代码:
CMainFrame *pFrame=(CMainFrame *)m_pMainWnd; CChildView *pView=(CChildView *)pFrame->GetActiveView(); pView->m_pToolBar=&(pFrame->m_wndToolBar);//注意:原m_wndToolBar是私有变量,要改成public的哦!
CView派生类是CMainFrame类的成员变量时,就很简单,直接在OnCreate()中设置

3、在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,"粘贴");//当菜单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);
好了!介绍完了。
大家可以在适当的地方(比如CMainFrame:OnCreate)中加入:
m_menu.SetHighLightColor(RGB(0,0,127));m_menu.SetTextColor(RGB(0,0,0));m_menu.SetBackColor(RGB(143,167,207));

来设置菜单颜,怎么样:) 欢迎大家多提建议
--  作者:admin
--  发布时间:2005-10-17 9:24:00

--  示例工程
CMenuEx.Zip
点击浏览该文件
--  作者:admin
--  发布时间:2005-10-17 10:08:00

-- 
www.vckbase/document/listdoc.asp?mclsid=3&sclsid=303
--  作者:admin
--  发布时间:2005-10-17 15:44:00

-- 
science/~corkum/BCMenu.html
--  作者:admin
--  发布时间:2005-10-17 17:48:00

-- 


我正在做一个历时很久的项目。出于某些原因,项目启动之初我们实现了自己的弹出式菜单。当工具提示信息出现之后,我们将这个功能引入了我们自己的菜单,以便当用户将鼠标停留在某个菜单项上的时候,能够出现相应的提示信息。这一功能对于我们的用户来说非常重要,因为 用它可以解释为什么某个菜单项是被禁用的。由于我们的用户对 Windows 平台越来越熟悉,他们想要外观上更标准的菜单。现在我们使用了 CMenu,但是我们失去了 出的菜单提示信息。请问如何在 MFC 中实现菜单提示信息呢?
Joakim Fagerli
多美妙的想法。Figure 1 的效果胜过千言万语。他展示了一个我写的菜单提示信息小程序——MenuTips,它实现了任何 MFC 应用程序均可复用的菜单提示信息。具备菜单提示信息特性真的很棒,因为它又排除了一个状态栏存在的理由。即便没有状态栏,你依然能够知晓每一个命令是做什么用的。更重要的是,提示信息 显示在每个菜单项旁边很更显眼。在当今的巨型显示器面前,很多用户甚至根本就意识不到出现在状态条上的菜单提示信息——它离人们的视线太远了。


Figure 1 菜单提示信息

  我在类 CMenuTipManager 里面实现了菜单提示。如果你想在自己的应用程序中使用菜单提示功能,只需要在主窗口类中添加一个 CMenuTipManager对象,然后在 创建框架的时候调用 Install 即可:
//in CMainFrame::OnCreate(...)m_menuTipManager.Install(this);   
  需要做的就这么多。现在当用户将鼠标停留在某个菜单项上面超过一秒钟,菜单提示信息管理器就会将对应的命令提示显示成一条提示信息,如 Figure 1 所示。CMenuTipManager 从你的程序的串表中获取提示信息,那也是 MFC 寻状态栏提示信息的地方。
  CMenuTipManager 使用了我闻名于世的子类化窗口类 CSubClassWnd 来捕获发往主窗口的 WM_MENUSELECT 消息。当用户在主菜单、系统菜单甚至上下文菜单中选中不同的的菜单项时,Windows 都会像 宿主窗口发送一个 WM_MENUSELECT 消息。如果你想提供反馈信息或者做其它自己你想做的事,此时便是最佳时机。MFC CFrameWnd::OnMenuSelect 处理 WM_MENUSELECT 消息以便在状态栏上显示命令提示信息。CMenuTipManager 捕获同样的消息来显示菜单提示信息,Figure 2 展示了相关的代码。
  总体上来说,CMenuTipManager 还是非常容易理解的,但是在 Windows 中还是有几点需要注意。首先是工具提示信息本身:有人曾指出过如何使用 Windows 标准的工具提示信息么?我在 2000 9 月和 2001 6 月的专栏中使用的是 CPopupText 类。CPopupText 非常简单,甚至一个知道如何敲分号的 VB 专家都能够实现它。 你只需要实例化一个 CPopupText 对象,调用 Create SetWindowText,然后 CPopupText::ShowDelayed 就会在指定的时间里显示提示信息了。CPopupText::Cancel 负责删除提示信息。唯一的难点是使 CPopupText 看起来和标准的工具提示信息一样。为了实现这一目的,CPopupText 使用了菜单字体并且调用 GetSystemColor(COLOR_INFOBK) 得到包含工具提示颜的系统颜。 具体细节请参考本文附带的源代码。
  对于 CMenuTipManager 而言,最复杂的部分是如何放置提示信息,以便恰好与高亮菜单项的右面对齐。这个问题基本思路是先得到菜单的位置,然后进行一系列的算术运算将所有的菜单项高度加起来,直到达到了被选中的菜单项。但是怎样才能得到菜单的位置呢?这可不是一个简单的问题。你也许猜到了,菜单本身也是一个窗口,但是没有 API 可以用来得到它的句柄,那怎么办呢?我曾经多次提到,在 Windows 中总会有解决办法,你决不会被困住的。
  CMenuTipManager 有一个静态的辅助函数 CMenuTipManager::GetRunningMenuWnd,它返回当前正在运行的菜单窗口。鉴于这个函数的使用频率非常高,我将其设定为公有。但这个函数是如何工作的呢?你也许考虑调用 WindowFromPoint 来得到位于鼠标下面的窗口。多数情况下这种方法能够达到目的,但是不要忽略一种情况:用户可能会通过键盘而非鼠标来调用菜单,此种情形下光标可能位于任何位置,而未必是在菜单上的。所以 CMenuTipManager 改为调用 ::EnumWindows 列举出所有顶层窗口,并且在其中寻一个使用了特殊类名 #32768Windows 为菜单窗口使用的类名)的窗口。
static BOOL MyEnumProc(HWND hwnd, LPARAM lParam){  char buf[16];  GetClassName(hwnd, buf, sizeof(buf));  if (strcmp(buf,"#32768")==0) { // menu window      // save hwnd      return FALSE; // no need to look further  }  return TRUE;      // keep looking}   
  因为只会显示一个菜单,所以 MyEnumProc 函数到的第一个就恰恰是我们需要的。即便由于某些非常古怪的原因,有两个菜单同时出现,EnumWindows 也会按照z轴上自顶向下的顺序列举窗口,所以第一个被到的菜单窗口也一定就是当前的活动菜单了。很聪明的做法不是么?一旦你到了菜单窗口(HWND 或者 CWnd),剩下的就只是为提示信息的出现位置进行一些像素运算了。Figure 2 中的 CMenuTipManager::OnMenuSelect 展示了细节工作。
  那么提示信息文本怎么样呢?CMenuTipManager 提供了另外一个辅助函数, CMenuTipManager::GetMessageString,用以得到与每一个菜单命令相关联的提示信息字符串。这个函数是我或多或少地从 CFrameWnd::GetMessageString 直接拷贝过来的。为什么要复制这个函数?这样一来你就可以在没有主框架的情况下调用它了。CFrameWnd::GetMessageString 应该是静态的,但是不知道哪 位友好的微软员工在编写这个函数时显然没有注意到根本不需要 CFrameWnd。为什么在加载字符串资源的时候一定需要通过主框架窗口?为了通用性,我 创建自己的静态版本函数。
  当我开始实现用户从菜单项上移开鼠标光标,提示信息必须消失的功能时,我遇到了另外一个非常奇怪的问题。对于主窗口而言,如果用户将鼠标指针移出菜单时,Windows 发送一个 WM_MENUSELECT 消息 ,并且在消息中附带有父菜单句柄和一个 MF_POPUP 标志,这样一来就有可能知道所发生的事情从而隐藏提示信息。但是对于上下文菜单来说,就没有那么幸运了。当用户将鼠标移出上下文菜单时,并没有 WM_MENUSELECT 消息通知你。
  没关系,在 Windows 中总会有解决方法。这种情况下,Windows 发送了一个不同的消息WM_ENTERIDLE。事实上,当程序等待输入并且对话框或者菜单被显示的时候,Windows 都会发送 WM_ENTERIDLE 消息。Windows 甚至通情达理到同时传递了对话框或者菜单的窗口句柄 HWND,吃惊吧?所以你所需要做的就是在接受到 WM_ENTERIDLE 消息的时候拿这个窗口句柄与鼠标下的窗口句柄进行比较。如果鼠标下的窗口句柄与随 WM_ENTERIDLE 发送过来的相同,那么鼠标仍然停留在菜单上面;如果鼠标下的是其 它窗口的句柄,那么说明用户已将鼠标移出上下文菜单,取消提示信息的时机到了。Figure 2 中的 CMenuTipManager::OnEnterIdle 函数完成的就是这个功能。
  最后,CMenuTipManager 使用了一个 m_bSticky 标记来控制提示信息是立即出现还是延迟一段时间之后才出现。当用户第一次使用某个菜单项的时候,菜单提示信息的出现是需要等待一段时间的。但是如果已经出现过一次提示信息,那么用户在选择其 它的新菜单项时就不必再等。所以一旦显示过提示信息,CMenuTipManager 就将 m_bSticky 置为 TRUE,以便随后的提示信息能够立即显示出来。取消菜单或者调用其 它命令将 m_bSticky 重新设置为 FALSE
  无论菜单项是处于启用还是禁用状态,CMenuTipManager 都会显示同样的提示信息。如果想在你的程序中显示为什么一个菜单项被禁用的信息,你就必须对 CMenuTipManager MFC 的相关机制做一些改动。MFC 期待命令字串具备长提示信息\\n短提示信息的格式,那意味着 MFC 总是预期有一个分隔长提示信息和短提示信息的换行符。MFC 将长提示信息显示在状态栏上、将短提示信息显示在工具栏上。你应该对这种处理方式进行扩展以便能够加入为什么菜单被禁 用的解释字符串。你必须将 CMenuTipManager::GetResCommandPrompt 函数改写为能够接受两个参数,以便适应命令提示信息为(long/short/disable)的格式,并且你需要改写 OnGetCommandPrompt 函数以便在菜单项有MF_DISABLED 标志时得到菜单项禁 用的提示信息。我将这部分工作留给读者作为练习。

祝大家编程愉快。
 
作者简介
  Paul DiLascia 是一位自由撰稿人、技术顾问和资深 Web/UI 设计师。他是 Windows ++Writing Reusable Windows Code in C ++ (Addison-Wesley, 1992) 一书的作者。你可以通过登录网站www.dilascia/
摘自 MSDN Magazine 2003 年 11 月刊。此杂志可通过各地的报摊购买,也可以订阅
--  作者:admin
--  发布时间:2005-10-17 17:49:00

-- 
完美实现真彩自绘菜单

作者:阿福(geforce_zf)
下载源代码一、提出问题

  在VCKBASE上读到《自绘菜单的实现》[作者:querw]。应用的我自己的正在进行的工程后发现效果不错,可是有存在许多问题。整个类的设计方面存在很多缺陷(先天,后天的),存在的主要问题如下:
1 当应用在多文档界面(MDI)中的时候,无法对系统自动添加菜单和文档模板菜单进行自绘(比如无法对文件->最近文件(MRU)菜单项中的文件列表就是系统自动添加)。原因是类内部没有对CMainFrame::OnInitPopupMenu()消息进行处理的函数, 因此不具备修改系统自动添加菜单项的功能。(BCMENU有这功能,而且工作的不错)
作者提到的 BCMENU 不用映射 WM_DRAWITEM WM_MEASUREITEM 两个消息就能实现自画功能,实际上是错误的。不映射这两个重要的消息,即使能自绘,也是有问题的,不信看图。
菜单编辑器中的模菜单样

使用BCMENU并且映射了这两个消息后的执行情况



使用BCMENU没有映射两个消息的执行情况



  原作者分析的自绘的是因为把主菜单(top-level menu)的子菜单都加载成弹出菜单(popupmenu),是不正确的。真正的原因是因为MFC框架会自动调用CMenu的两个虚拟函数MeasureItem()OnDrawItem() 因此,当CMenuEx派生于CMenu,并且重写这两个虚拟函数以后。
2 1MFC框架调用的GetMenu()->MeasureItem()就相当于调用了CMenuEx::MeasureItem(),从而实现自绘菜单控件尺寸的测量。
2MFC框架调用GetMenu()->DrawItem()就相当于调用了CMenuEx::DrawItem()来实现自绘菜单控件的自绘操作(不懂??,这正是C++的虚拟的妙用,指向派生类对象的基类指针可以调用派生类的虚拟函数,多么伟大的发明,谁想出来的???)。与子菜单是否为弹出菜单(popupmenu)没有什么关系。以下是摘自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 {  ...... } ......}     
3 当菜单项中含有子菜单(submenu),而不含有分割条的时候,子菜单项的高度不可调。原因为原CMenuEx程序中将分割条的原COMMAND ID0)改为菜单项的COMMADN ID(-1), 以欺骗MFC框架调用CMenuEx::MeasureItem()来计算子菜单项(submenu)的高度。(很令我失望,这也是促使我自己动手重写该类的原因之一。不信看程序,看图)
摘录自原CMenuEx.cpp546-560
if(uID == 0) //分隔符{ ::AppendMenu(hNewMenu,MF_SEPARATOR,0,NULL); ...... // 注意,就是下面那个-1,把分割条的ID0改到-1        // 从而是MFC框架误以为到了ID为-1的菜单项,并且测量了它的尺寸 // 而实际上ID为-1的菜单项是不可能被void CWnd::OnMeasureItem()到的 ::ModifyMenu(hNewMenu,i,MF_BYPOSITION | MF_OWNERDRAW,-1,(LPCTSTR)pMenuItem);}     
4 菜单编辑器中没有分割条菜单的菜单



CMenuEx执行的模样



菜单编辑器中有分割条菜单的菜单



CMenuEx执行的模样

代码不够简练,程序粒度划分不好,可读性差(不过比BCMENU的代码可读性强多了:))。
二、解决问题

  针对以上遇到的问题,我参考BCMENU和原作者的CMenuEx,对CMenuEx类重新进行了组织,类定义如下:
// 声明,因为下面的结构要用到 CMenuEx*,又不支持向后引用,又什么办法啊!class CMenuEx;//自绘菜单数据项结构,就是要传给系统的那个牛XLPCTSTR指针所指向的东东class CMenuEx : public CMenu{ DECLARE_DYNAMIC( CMenuEx )  // Constructorpublic:  CMenuEx();  virtual ~CMenuEx(); virtual BOOL DestroyMenu(); // Operationpublic: // 加载菜单操作 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); // Attributesprotected: // 指示为主菜单(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);  // Implementationpublic: // 绘制菜单项的虚拟函数,由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 Memberpublic: // CMainFrameOnMeasureItem()消息映射函数中调用它,用来测量所有菜单项尺寸 static void MeasureItem(LPMEASUREITEMSTRUCT lpMIS);  // CMainFrameOnInitPopupMenu()消息映射函数中调用它, // 用来更新系统自动添加的菜单项为自绘菜单 static void InitPopupMenu(CMenu* pPopupMenu,UINT nIndex,BOOL bSystem); };#endif // !defined(MENUEX_H)   
三、实现方法

  有了以上的强有力的武器,就可以对我们的程序下手了:)在MDISDI中使用CMenuEx的时候需要修改以下地方。
先将MenuEx.hMenuEx.cpp添加到工程中,在CMainFrame中添加头文件,CMenuEx对象,用于存储菜单图像的CImageList对象和初始化菜单程序。
#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;  // 菜单项被选中时的图像列表 ...}     
撰写菜单图像索引表,初始化菜单程序,初始化菜单图像列表程序, 和两个重要的消息映射函数CMainFrame::OnMeasureItem()CMainFrame::OnInitPopupMenu() (什么?不会添加!,Clazard帮忙或许有点帮助了:))
// 声明,因为下面的结构要用到 CMenuEx*,又不支持向后引用,又什么办法啊!class CMenuEx;//自绘菜单数据项结构,就是要传给系统的那个牛XLPCTSTR指针所指向的东东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 // 如果bIsSubMenuTRUE,该值为指向子菜单项的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               
5 当然,要通过资源编辑器的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成员,并且已经设置了真彩位图 // 关于设置工具栏的真彩位图,请参考 www.vckbase/document/viewdoc/?id=576 //"CMDIFrameWnd::OnMeasureItem()",不对子菜单项的尺寸进行测量 // 害的我们只好映射这个函数了  CMDIFrameWnd::OnMeasureItem(nIDCtl, lpMeasureItemStruct); // 静态函数,看好了,别忘了写CMenuEx CMenuEx::MeasureItem(lpMeasureItemStruct);}     
6 CXXXApp::InitInstance()中添加代码,XXX代表你自己的程序了
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);  // 要添加的代码到这结束  ......}     
三、总结

说了这么多,也不知道大家看明白没有,没关系,先贴个图,大家看看效果再说了。

效果图一,使用图像索引表加载的小图标菜单



效果图一,工具条加载的大图标菜单

四、结束语

  感谢querwBCMenu的作者,没有他们的辛勤劳动,后人是没办法站在他们肩膀上的!由于程序写的匆忙,难免有不尽人意和错误的地方,欢迎大家任意修改源程序:) 要说这个菜单做的完美,那是吹牛,世界上哪有完美的东西啊 :) 只要自己觉得完美,就够了。 希望大家能从文章中学到点东西,就好。
--  作者:admin
--  发布时间:2005-10-17 17:50:00

-- 
老大 你还敢用完美2字。用了你的代码出现了一大堆内存泄漏!!!!!
看看DestroyMenu()里面,错误一大堆

for(int i = 0; i < m_MenuItemArr.GetSize(); i++)
{
MENUITEM *pMenuItem = m_MenuItemArr.GetAt(i);

// destroy submenu
if(pMenuItem->bIsSubMenu)
pMenuItem->pSubMenu->DestroyMenu(); 

// remove menu item from array
m_MenuItemArr.RemoveAt(i);

// free menuitem struct
delete pMenuItem;
}

要这样改

for(int i = 0; i < m_MenuItemArr.GetSize(); i++)
{
MENUITEM *pMenuItem = m_MenuItemArr.GetAt(i);

// destroy submenu
if(pMenuItem->pSubMenu)
{
pMenuItem->pSubMenu->DestroyMenu(); 
delete pMenuItem->pSubMenu; // DestroyMenu()了以后就不用delete了嗦
}

// remove menu item from array
//m_MenuItemArr.RemoveAt(i);  // 就算你要Remove也要--i一下啊

// free menuitem struct
delete pMenuItem;
}

m_MenuItemArr.RemoveAll(); // 这里再RemoveAll
( cooljjyy 发表于 2005-4-23 20:52:00)

--  作者:admin
--  发布时间:2005-10-18 0:32:00

--  告别图标失真的烦恼



示例代码运行效果图

自从和VC知识库认识之后我认识了不少朋友,有的象我一样走着苦苦探索的道路。其中有很多朋友问我在VC中如何保证256以上的图标加载后不失真。根据这些提问我也曾经到各个站点上寻求答案,结果是大失所望,虽然有介绍如何实现256以上的工具栏的文章,但是方法中大都采用加载一幅256以上的工具栏位图的方法。这样的方法存在一个麻烦就是:有什么简便好用的制图工具来做这样的位图呢?我虽然没有尝试过去寻这样的工具,但是我知道大家都希望有更直接的方法来使用256以上的图标,并且保持最终的效果不失真!

所以针对以上问题我研究了MSDN,费了九牛二虎的劲最终才"修成正果",哈哈!不过有快乐得和大家一起分享,希望我的努力没有白费,好了,废话少说,咱们立刻揭开它神秘的面纱吧。

. 建立一个单文档工程(多文档也可)
. 在主框架CMainFrame类的头中定义一个CImageList对象。
. 在主框架CMainFrame类的OnCreate处理中创建CImageList对象并且向其中添加你想要的图标。

其中CImageList的创建是本工程最最关键的一步,如果写错,那你就回到原处,结果是空空如也。哈哈!
m_ilTB.Create(32, 32, TRUE | ILC_COLOR8, 4, 0); //设置ToolBar的图标列表
现在对这几个参数做个详细介绍如下:
1 前两个参数指定图标的宽度和高度,即:图标的尺寸定义。
2 第三个参数指定何如显示图标。ILC_ COLOR8说明以256调板来显示图标。而TRUE则指明了以透明方式来显示图标。那么两者进行"按位或"运算后的意义变为:以透明方式来显示256图标。
3 第四和第五个参数则分别为:初始图标个数和新增图标时对象自动申请内存空间的步长。如果在大批量操作图标,并且需要不断的增删图标时,设置第五个参数可以改变程序的性能,如果第五个参数设置的比较适中则可以避免程序反复的申请和释放内存空间。

. 最后的一步就是加载和添加图标资源了。
HICON hIcon = NULL;hIcon = (HICON)::LoadImage(::AfxGetInstanceHandle(), MAKEINTRESOURCE(IDI_ICON1), IMAGE_ICON, 32, 32, 0); m_ilTB.Add(hIcon);
好了,一切就是这么简单,如果你还不清楚的话,那就打开工程看看吧,如你有什么问题也不要忘记来信告诉我哦!最后祝大家学习愉快,多多交流,多多进步,一切顺利!

发表于 @ 20061228日 10:06:00 | 评论(   )举报| 收藏
旧一篇:OutLook样式类使用过程,弹出模式对话框时候,鼠标不能点击对话框按钮,的问题。 | 新一篇:解决了CListCtrl控件当字体改变后,自动调整每行的宽度的问题.
查看最新精华文章 请访问博客首页相关文章
发表评论
情:
评论内容:
名:
码:
重新获得验证码
热门招聘职位