弹出式菜单(PopMenu)大家都熟悉,在WIN98的桌面上单击鼠标右键弹出的菜单就是弹出式菜单。通常情况下,弹出式菜单在鼠标右键单击时弹出,当然,也可以根据需要随时弹出。 在VC++5的MFC中,管理菜单的类叫CMenu。下面我向大家介绍建立一个弹出式菜单的基本方法。 一、在资源编辑器中建立一个菜单资源
新建一个菜单资源,比如把菜单的ID号为IDC_POPMENU。此菜单有一项两层,即有一个可弹出的菜单项,而此菜单项的弹出内容即为将要建立的弹出式菜单的内容。如右图,“可弹出项”下的菜单即为将要建立的弹出式菜单的内容。实际上,“可弹出项”这个名称在以后的操作中不会被用到,但VC++5不允许直接建立弹出式菜单,所以采用先建立“可弹出项”的方法。
至于每一个菜单项的消息映射,与一般的菜单相同。
二、使用CMenu类对象
CMenu类的成员函数较多,但建立弹出式菜单只需用到其中几个成员函数。
1、LoadMenu函数
原型:BOOL LoadMenu( UINT nIDResource ); 其中nIDResource是菜单资源的ID号,这里用的是刚建立的IDC_POPMENU。 2、GetSubMenu函数
原型:CMenu* GetSubMenu( int nPos ) const;
此函数用于得到子菜单的指针。nPos为层数,0为第一层子菜单……以此类推。
由于我们需要的是“可弹出项”的第一层子菜单,因此用GetSubMenu(0)来得到第一层子菜单的类指针。 3、TrackPopupMenu函数
原型:BOOL TrackPopupMenu( UINT nFlags,int x,int y,CWnd* pWnd,LPCRECT lpRect = NULL );
其中:
nFlags为屏幕坐标属性和鼠标坐标属性
屏幕坐标属性:
TPM_CENTERALIGN 横向将菜单以x居中
TPM_LEFTALIGN 横向将菜单以x左对齐
TPM_RIGHTALIGN 横向将菜单以x右对齐
鼠标按键属性(只在响应WM_CONTEXTMENU消息时有效):
TPM_LEFTBUTTON 连续按? 右键不会连续弹出菜单,鼠标右键不可用于选定菜单项
TPM_RIGHTBUTTON 连续按鼠标右键会连续弹出菜单,鼠标右键可用于选定菜单项
x,y均为屏幕坐标
lpRect 菜单所占的区域。如果为NULL,当用户在菜单以外的区域按鼠标键时,菜单会消失
三、实例
1、当鼠标右键单击程序窗口的客户区时,程序会收到一条WM_CONTEXTMENU消息,此时是弹出菜单的最好时机
用ClassWizard中的“Add Windows Message Handler”功能添加对WM_CONTEXT消息的响应函数,函数中代码如下:
void CMyDlg::OnContextMenu(CWnd* pWnd, CPoint point)
{
CMenu menu; //定义CMenu类对象 menu.LoadMenu(IDC_POPMENU); //装入刚建立的菜单IDC_POPMENU menu.GetSubMenu(0)->TrackPopupMenu(TPM_LEFTALIGN,point.x,point.y,pWnd); /*GetSubMenu(0)得到IDC_POPMENU的第一层子菜单,TrackPopupMenu将菜单弹出到(x,y)处。由于设置为TPM_LEFTALIGN,所以菜单以(x,y)为左上角。*/
}
2、在其他时候弹出菜单也可以,比如,可以响应WM_LBUTTONDOWN消息。这样,在鼠标左键单击时也能弹出菜单
用ClassWizard中的“Add Windows Message Handler”功能添加对WM_LBUTTONDOWN消息的响应函数,函数中代码如下: void CMfc5Dlg::OnLButtonDown(UINT nFlags, CPoint point)
{
CMenu menu; //定义CMenu类对象 menu.LoadMenu(IDC_POPMENU); //装入刚建立的菜单IDC_POPMENU ClientToScreen(&point); menu.GetSubMenu(0)->TrackPopupMenu(TPM_LEFTALIGN,point.x,point.y,this); /*GetSubMenu(0)得到IDC_POPMENU的第一层子菜单,TrackPopupMenu将菜单弹出到(x,y)处。由于设置为TPM_LEFTALIGN,所以菜单以(x,y)为左上角。*/
ScreenToClient(&point); CDialog::OnLButtonDown(nFlags, point);
}
注意:在WM_LBUTTONDOWN消息中得到的point对象所存的坐标是相对于窗口客户区的,而TrackPopupMenu中的x,y需要是相对于屏幕的,所以需用ClientToScreen函数进行转换,但此消息响应函数要调用CDialog::OnLButtonDown(nFlags, point),故应该用ScreenToClient函数将point所存的坐标还原为相对于窗口客户区的。 第二篇: 个菜单条总是属于一个窗口,当用户选定一个菜单项后,系统向菜单的父窗口发出消息,如果是普通菜单发送WM_COMMAND消息,如果是系统菜单发送WM_SYSCOMMAND消息。当鼠标悬浮在一个有下级菜单的菜单项上时,系统首先向菜单的父窗口发送WM_INITMENUPOPUP消息,然后展开子菜单。每一个菜单条由一个唯一的句柄指向,类型为HMENU。获得一个菜单的句柄使用GetMenu()函数,获得子菜单用GetSubMenu()、GetMenuItemInfo()。获得窗口的系统菜单句柄应当使用GetSystemMenu()。菜单除了句柄,还有ID,类似于按钮的ID,当菜单触发事件时,菜单ID被包含在WM_COMMAND或WM_SYSCOMMAND中一同发送。可以通过GetMenuItemID()获取一个菜单项的ID。访问菜单项时如果使用ID的方式会比较麻烦,一种变通的方法就是按照菜单项的以0为基数的索引进行访问,最左边的为0,向右逐次递增,最上边的为0,向下逐次递增。
一、 菜单的建立
1、 使用菜单资源
首先设计好菜单,然后用LoadMenu()加载,再用SetMenu()设置,例如,在窗口的WM_CREATE消息中:
HMENU hMenu; hMenu=LoadMenu(hInst,MAKEINTRESOURCE(IDR_MENU1)); SetMenu(hWNd,hMenu); | 2、 内存菜单模板建立菜单
有的应用程序允许用户自定义菜单,这就要求使用内存菜单模板来定义菜单,然后用LoadMenuIndirect()函数加载这个内存菜单模板获得菜单句柄,内存菜单模板由两部分组成:一个MENUITEMTEMPLATEHEADER(或者MENUEX_TEMPLATE_HEADER)结构和若干个MENUITEMTEMPLATE(或者MENUEX_TEMPLATE_ITEM)结构
3、 CreateMenu()函数
CreateMenu()函数返回一个指向空菜单的HMENU指针,利用这个指针向其中添加菜单项InsertMenuItem()
二、 菜单的显示
1、 对于已经通过在WNDCLASSEX窗口类注册时指定hMenu成员或者调用SetMenu()函数为其设定了父窗口的菜单,窗口显示,菜单自动显示
2、 显示弹出菜单应使用TrackPopmenuEx()函数,例如
HMENU hpMemu; hpMenu=LoadMenu(hInst,MAKEINTRESOURCE(IDR_POPMENU); TrackePopmenuEx( hpMenu,//菜单的句柄 TPM_LEFTALIGN,//菜单和弹出点坐标的对齐方式 x,//弹出点X坐标 y,//弹出点Y坐标 hWnd,//父窗口句柄 NULL); | 3、 菜单显示过程中的消息
用户单击菜单栏上的一项,父窗口接收到WM_INITITEM消息(在这个消息中可以绘制菜单),然后弹出菜单。当鼠标指向一个可以弹出子菜单的菜单项时,父窗口接收到WM_INITMENUPOPUP(可捕获此消息绘制子菜单),然后弹出子菜单。当鼠标在菜单上移动时,父窗口接收到WM_MENUSELECT消息,该消息中包含有当前的菜单项的索引号,可以对其进行引用。单击一个菜单项,产生WM_COMMAND消息,单击一个风格为MNS_NOTIFYBYPOS的菜单项,产生WM_MENUCOMMAND消息,该消息除了提供WM_COMMAND消息提供的数据外还额外添加了一个MENUINFO结构。右键弹出菜单发送WM_CONTEXTMENU消息。用户在一个菜单项上右击鼠标将产生WM_MENURBUTTONUP消息(例如在IE的收藏菜单的菜单项上右击弹出菜单的效果就可以通过捕获该消息实现) 三、 菜单的动态更改
1、 插入新的菜单项InsertMenuItem()
首先填写MENUINFO结构
typedef struct tagMENUITEMINFO { UINT cbSize; 结构的大小,sizeof(MENUINFO) NT fMask; 将要获得或者设置的项目,后面的哪些参数发挥作用受该参数的设置的影响。MIIM_BITMAP,hbmpItem将用来设置或返回按钮的位图,MIIM_ID,wID设置或返回菜单项的ID UINT fType; 设置或返回菜单项的类型,MFT_BITMAP位图;MFT_STRING字符串;MFT_OWNERDRAW,发送WM_DRAWITEM和WM_MEASUREITEM消息用于绘制菜单项 UINT fState; 菜单项的状态:MFS_DEFAULT,默认;MFS_GRAYED,不可用; UINT wID; 菜单项的ID,要求fMask中有MIIM_ID HMENU hSubMenu; 下一级菜单的句柄,要求fMask中有MIIM_SUBMENU HBITMAP hbmpChecked; HBITMAP hbmpUnchecked; ULONG_PTR dwItemData; LPTSTR dwTypeData; UINT cch; HBITMAP hbmpItem; } MENUITEMINFO, *LPMENUITEMINFO; 然后调用InsertMenu()函数 InsertMenu( HMENU hMenu; UINT nItems; BOOL isPisition;//设为TRUE,nItems为菜单项的索引号,设为FALSE,nItems为菜单项的ID LPCMENUITEMINFO lpmiinfo; ) | 2、 修改菜单项属性使用SetMenuItemInfo()函数
3、 删除菜单项
DeleteMenu()和RemoveMenu(),菜单项删除后用DrawMenuBar()重绘菜单显示更新
四、 菜单特效
1、 快捷菜单
捕获父窗口的WM_CONTEXTMENU(用户在窗口中右击鼠标将激活该事件),调用函数 TrackPopupMenuEx();TrackPopupMenuEx(hm, 0,GET_X_LPARAM(lParam),GET_Y_LPARAM(lParam),hWnd,NULL);hm为菜单句柄,GET_X_LPARAM(不知怎么不能用)宏获得事件发生时鼠标的X坐标,hWnd为父窗口句柄
2、 位图菜单
MENUITEMINFO结构的fMask中添加MIIM_BITMAP标志,为期hbmItem指定一个指向位图的指针
五、 自绘制菜单
1、 指定标志
要实现自绘制必须使菜单项的风格符合要求,有两种方法可以做到,其一是用InsertMenuItem()函数插入,其二是用SetMenuItemInfo()函数修改,不论使用哪一个都必须填写一个MENUITEMINFO结构,为其fMask添加MIIM_FTYPE,为其fType添加MFT_OWNERDRAW。和列表框一样,对菜单项进行自绘制是设计到较多的数据传递,可以存放在MENUITEMINFO结构中,设置fMask为MIIM_DATA,然后给dwItemData成员赋值。WM_DRAWITEM(LPDRAWITEMSTRUCT)和WM_MEASUREITEM(LPMEASUREITEMSTRUCT)的参数中都含有itemData成语用于取出预先存放的数据。
例如:
MENUITEMINFO mi; HMENU hmn; mi.fMask=MIIM_FTYPE; milfType=MFT_OWNERDRAW; hmn=GetMenu(hWnd); SetMenuItemInfo(hmn,0,TRUE,&mi); | 2、 处理WM_MEASUREITEM消息
用户单击,菜单即将显示时,菜单的父窗口接收到WM_MEASUREITEM消息,捕获此消息可以设置菜单项的尺寸,例如:
LPMEASUREITEMSTRUCT lpmis; lpmis=(LPMEASUREITEMSTRUCT)lParam; lpmis->itemHeight=48 lpmis->itemWidth=144; | 3、 处理WM_DRAWITEM消息
WM_DRAWITEM消息的LPARAM参数为一个LPDRAWITEMSTRUCT指针,其中含有指向被绘制的菜单项的设备场景指针、菜单ID和状态等信息,根据这些信息进行绘制,例如:
LPDRAWITEMSTRUCT lpdis; HDC hMem; HBITMAP hbm; hMem=CreateCompatibleDC(lpdis->hDC); hbm=LoadBitmap(hInst,MAKEINTRESOURCE(IDB_MENUMAP)); SelectObject(hMem,hbm); BitBlt(lpdis->hDC,0,0,lpids->rcItem.right,lpdis->rcItem.bottom,hMem,0,0,SRCCOPY); | |