现在的位置: 首页 > 自动控制 > 工业·编程 > 正文

MFC中CMenu类的讲解

2012-08-04 23:08 工业·编程 ⁄ 共 4463字 ⁄ 字号 暂无评论

    在MFC中,排除调用API函数那种复杂的方法之外,就只有CMenu这个类可以让我们来控制菜单了。对于这个类,琢磨了两天,总算有点心得。

   对于系统菜单,创建起来比较简单,直接使用资源编辑器就能生成菜单,再通过ClassWizard创建菜单命令函数。在我的资源中上传了一个工程,实现了一个右键弹出贴图菜单。结合这个工程,介绍动态创建菜单、创建弹出式菜单和重绘菜单。

首先介绍基础知识:

一、CMenu类的成员函数:

1. CreateMenu()和CreatePopupMenu(),这两个函数用来创建一个菜单实例,CreateMenu()创建的是普通的菜单实例,如果想创建弹出式菜单,就要用CreatePopupMenu()函数。

2. AppendMenu()向菜单中添加一个子项,这个函数有两个主要的参数。第一个UINT nFlags,这个参数表明了该子项的属性特征,可以这样说,这个参数规定了菜单的样式和功能。后面会详细讲这个参数所能使用的值。第二个参数UINT_PTR nIDNewItem,根据nFlags使用不同的设置,该参数将标明菜单的资源ID或在这个菜单中的索引号。第三个参数可以省略,如果不省略,可以传入一个字符串,这个字符串将显示在菜单中(因为我准备用图片表示菜单项,所以我的工程中省略了这个参数)。

3. DrawItem(),这是一个虚函数,如果菜单设置成可以自绘类型,则这个函数将在生成菜单、弹出菜单、选中菜单、点击菜单等时由系统框架调用。因此,这个函数是一个很有用的函数,它可以帮你绘制出各种样式的菜单。

4. MeasureItem()也是一个虚函数,当菜单被创建的时候由系统框架调用。这个函数用来设置菜单的大小。

二、nFlags说明:

只有当nFlags设置成MF_OWNERDRAW的时候,系统框架才会重绘菜单。

MF_CHECKED:命令旁显示默认复选标志

MF_UNCHECKED:清除命令旁的复选标志

MF_DISABLED:禁止此菜单命令,但是不变灰显示

MF_ENABLED:允许此菜单命令,恢复到正常状态

MF_GRAYED:禁止此菜单命令,变灰显示

MF_MENUBARBREAK:对于静态菜单,放到新行;对于弹出菜单,放到新栏 中,栏间有分隔线

MF_MENUBREAK:对于静态菜单,放到新行;对于弹出菜单,放到新栏,栏间雾分隔线

MF_OWNERDRAW:指定该命令是自画式菜单命令

MF_POPUP:指定该菜单命令有一个关联的弹出式菜单

MF_SEPARATOR:画一条水平分隔线,只用于弹出式菜单

MF_STRING:指定此菜单命令是一个字符串

三、开始工程:

1. 创建一个基于对话框的工程。然后添加一个新类,这个新类从CMenu继承。这个地方值得注意:如果使用ClassWizard添加新类的话,你会发现在基类的列表中找不到CMenu。所以,只能自己创建两个文件,然后自己写代码了。

2. 我们给新类取名CBmpMenu,重载CMenu的DrawItem()和MeasureItem()这两个虚函数。

头文件:

class CBmpMenu : public CMenu

{

public:

CBmpMenu();

virtual ~CBmpMenu();

virtual void DrawItem(LPDRAWITEMSTRUCT lpDIS);

virtual void MeasureItem(LPMEASUREITEMSTRUCT lpMeasureItemStruct);

};

3.

void CBmpMenu::MeasureItem(LPMEASUREITEMSTRUCT lpMeasureItemStruct)

{

          lpMeasureItemStruct->itemWidth=50;

          lpMeasureItemStruct->itemHeight=18;

}

可以看的很清楚,lpMeasureItemStruct->itemWidth表示菜单的宽度, itemHeight表示菜单的高度,重新设置你想设置的值。

4.工程添加了两组图片资源,每一组五张,一组表示普通模式下的图片,一种表示当选中菜单时显示的图片。要使菜单弹出时显示图片,则重载DrawItem函数,并在DrawItem函数中绘制图片。

void CBmpMenu::DrawItem(LPDRAWITEMSTRUCT lpDIS)

{

       CDC* pDC=CDC::FromHandle(lpDIS->hDC);

       CDC dcmem;

       dcmem.CreateCompatibleDC(pDC);

       CBitmap * bt=new CBitmap;

       if((lpDIS->itemState & ODS_SELECTED) &&

              (lpDIS->itemAction & (ODA_SELECT | ODA_DRAWENTIRE)))

       {

              switch(lpDIS->itemID)

              {

              case IDR_MENU:

                     bt->LoadBitmap(MAKEINTRESOURCE(IDB_HOT_50));

                     break;

              case IDR_MENU+1:

                     bt->LoadBitmap(MAKEINTRESOURCE(IDB_HOT_100));

                     break;

              case IDR_MENU+2:

                     bt->LoadBitmap(MAKEINTRESOURCE(IDB_HOT_150));

                     break;

              case IDR_MENU+3:

                     bt->LoadBitmap(MAKEINTRESOURCE(IDB_HOT_200));

                     break;

              case IDR_MENU+4:

                     bt->LoadBitmap(MAKEINTRESOURCE(IDB_HOT_FULL));

                     break;

              }

       }

       else

       {

              switch(lpDIS->itemID)

              {

              case IDR_MENU:

                     bt->LoadBitmap(MAKEINTRESOURCE(IDB_NORMAL_50));

                     break;

              case IDR_MENU+1:

                     bt->LoadBitmap(MAKEINTRESOURCE(IDB_NORMAL_100));

                     break;

              case IDR_MENU+2:

                     bt->LoadBitmap(MAKEINTRESOURCE(IDB_NORMAL_150));

                     break;

              case IDR_MENU+3:

                     bt->LoadBitmap(MAKEINTRESOURCE(IDB_NORMAL_200));

                     break;

              case IDR_MENU+4:

                     bt->LoadBitmap(MAKEINTRESOURCE(IDB_NORMAL_FULL));

                     break;

              }

       }

       dcmem.SelectObject(bt);

       pDC->BitBlt(lpDIS->rcItem.left,lpDIS->rcItem.top,61,18,&dcmem,0,0,SRCCOPY);

}

LPDRAWITEMSTRUCT类型的参数包含9个域:

typedef struct tagDRAWITEMSTRUCT {

   UINT CtlType;

   UINT CtlID;

   UINT itemID;

   UINT itemAction;

   UINT itemState;

   HWND hwndItem;

  HDC hDC;

   RECT rcItem;

   DWORD itemData;

} DRAWITEMSTRUCT;

CtlType:表示重绘的对象,因为不光菜单可以重绘,其他的一些资源也可以。ODT_BUTTON、ODT_COMBOBOX  、ODT_LISTBOX 、ODT_MENU  、ODT_LISTVIEW   、ODT_STATIC   、ODT_TAB 。

CtlID:控件资源的ID号,如果对菜单重绘,那么这个域不使用。

itemID:在这里表示菜单项的ID。

itemAction:表示发生什么动作导致重绘。

itemState:表示当前重绘资源所处的状态,例如被点击,被选中。

各种动作和状态MSDN中做了详细说明,可在MSDN2003:Visual Studio . Net/Visual C++/ Visual C++参考/ Visual C++ libraries/MFC Reference/Structures…/Structures by MFC/ DRAWITEMSTRUCT Structure中找到。

hwndItem:控件窗体的句柄,如果是菜单,则表示菜单的句柄。

hDC:要重绘的区域的设备内容,重绘的所有动作都靠它。

rcItem:重绘区域的坐标。

itemData:添加菜单时设定的值,由于我们省略了AppendMenu()的第三个参数,所以这里不用关心它。

看完这些介绍,我们大致可以明白如何重新绘制菜单了,也就是使用hDC在rcItem区域绘图,至于绘制什么图,可以根据itemID表示的不同的子项,itemAction表示的不同的动作,itemState表示子项的不同的状态来绘图。

此工程就是根据是否选中菜单『if((lpDIS->itemState & ODS_SELECTED) &&(lpDIS->itemAction & (ODA_SELECT | ODA_DRAWENTIRE)))』绘制不同状态的图。

同时判断一下ID,switch(lpDIS->itemID)。

给我留言

留言无头像?