1、菜单相关问题
Win32--HMENU
MFC--CMenu类对象
2、相关类
CMenu - 封装了关于菜单的各种操作,还封装了一个非常重要的成员
m_hMenu(菜单句柄)
3、菜单项被点击的处理 WM_COMMAND 消息
ON_COMMAND
4、程序的类对菜单命令的响应顺序
顺序依次是:视图类->文档类->框架类->应用程序类
菜单命令消息路由的过程:点击某个菜单项,框架类最先接收到这个命令消息,并交给视图类,视图类查找自身是否对此消息进行了响应,如果响应了,就用相应的响应函数对消息进行处理,路由过程结束,如果没有响应,就交给文档类;文档类同样查找自身是否对此消息进行了响应,如果响应了,就用相应的响应函数对消息进行处理,路由过程结束,如果也没有响应,就交还给视图类,视图类又交还给框架类;这时,框架类才查看自身是否对此消息进行了响应,如果也没有响应就把消息交给最后的应用程序类来进行处理
5、菜单的使用
5.1添加菜单资源
5.2设置菜单栏
在框架类窗口CFrameWnd的消息响应函数OnCreate中设置菜单栏的装载和移除
BOOL SetMenu(CMenu* pMenu);
1)装载菜单栏
CMenu menu;
menu.LoadMenu(菜单资源ID); -加载菜单,并将对象和句柄进行绑定
SetMenu(&menu);
menu.Detach();
2)移除菜单栏
SetMenu(NULL);
5.3菜单捕获
1)获取程序的菜单栏:CMenu* GetMenu() const;
2)获取顶层菜单项:CMenu* GetSubMenu(int nPos) const;
如:GetSubMenu(0)
5.4设置菜单项的状态
ON_WM_INITMENUPOPUP
标记菜单项: ::CheckMenuItem / CMenu::CheckMenuItem
UINT CheckMenuItem(UINT nIDCheckItem,UINT nCheck);
nIDCheckItem:指定需要处理的下拉菜单项,值可为位置索引号(如:0)或者菜单项ID号(如:(UINT)ID_FILE_NEW)
nCheck:设置菜单项 | 如何定位该菜单项,
包括:MF_CHECKED/MF_UNCHECKED | MF_BYPOSITION/MF_BYCOMMAND
例:GetMenu()->GetSubMenu(0)->CheckMenuItem(0,MF_CHECKED|MF_BYPOSITION);
或者:GetMenu()->GetSubMenu(0)->CheckMenuItem(ID_FILE_NEW,MF_CHECKED|MF_BYCOMMAND);
默认菜单项: ::SetDefaultItem / CMenu::SetDefaultItem
BOOL SetDefaultItem(UINT uItem,BOOL fByPos=FALSE);
uItem:指定需要处理的下拉菜单项,值可为位置索引号(如为-1则表明没有默认菜单项)或者菜单项ID号(如:(UINT)ID_FILE_NEW)
fByPos:是BOOL类型,若为FALSE,则第一个参数须为菜单项ID号,否则为位置索引号
例:GetMenu()->GetSubMenu(0)->SetDefaultItem(1,TRUE);
图形标记菜单: ::SetMenuItemBitmaps / CMenu::SetMenuItemBitmaps
BOOL SetMenuItemBitmaps(UINT nPosition,UINT nFlags,const CBitmap* pBmpUnchecked,const CBitmap* pBmpChecked);
nPosition:指定需要处理的下拉菜单项,值可为位置索引号(如为-1则表明没有默认菜单项)或者菜单项ID号(如:(UINT)ID_FILE_NEW)
nFlags:MF_BYPOSITION/MF_BYCOMMAND
pBmpUnchecked:指定当取消菜单项选中状态时的位图
pBmpChecked:指定选中菜单项时显示的位图
例:m_bitmap.LoadBitmap(IDB_BITMAP1);
GetMenu()->GetSubMenu(0)->SetMenuItemBitmaps(0,MF_BYPOSITION,&m_bitmap,&m_bitmap);
图形标记的大小应为:16x16
禁用菜单项: ::EnableMenuItem / CMenu::EnableMenuItem
UINT EnableMenuItem(UINT nIDEnableItem,UINT nEnable);
nIDEnableItem:指定需要处理的下拉菜单项,值可为位置索引号(如为-1则表明没有默认菜单项)或者菜单项ID号(如:(UINT)ID_FILE_NEW)
nEnable:MF_DISABLED/MF_ENABLED/MF_GRAYED | MF_BYPOSITION/MF_BYCOMMAND
注意:若要更改MFC自动创建的菜单项的状态,须修在程序框架窗口类的构造函数中添加:m_bAutoMenuEnable=FALSE;
例:GetMenu()->GetSubMenu(0)->EnableMenuItem(2,MF_DISABLED|MF_GRAYED|MF_BYPOSITION);
5.5MFC默认创建的菜单项状态维护
利用MFC编程时,对于自动创建好的菜单项,可利用MFC类向导的UPDATE_COMMAND_UI消息来改变菜单项状态(只能应用于下拉菜单项),仅需对相应的菜单项的ID生成消息响应函数;
在UPDATE_COMMAND_UI消息的响应函数中可调用CCmdUI的相应函数如:Enable/SetCheck/SetText,分别实现使菜单项可用或禁用/设置标记菜单/设置菜单项的文本的功能
void CMainFrame::OnUpdateEditCut(CCmdUI* pCmdUI) { // TODO: Add your command update UI handler code here pCmdUI->Enable(); }
工具栏上的各工具按钮和下拉菜单项的ID标识是相同的,改变菜单项的状态工具栏上相应的工具按钮状态也会改变;所以,如果要把工具栏的一个工具按钮与菜单项中的某个菜单项相关联,只要将它们的ID设为同一个就可以了;
void CMainFrame::OnUpdateFileNew(CCmdUI* pCmdUI) { // TODO: Add your command update UI handler code here pCmdUI->Enable(FALSE); }
5.6上下文菜单(右键菜单)
ON_WM_CONTEXTMENU
CMenu::TrackPopupMenu / ::TrackPopupMenu -显示弹出式菜单
BOOL TrackPopupMenu(UINT nFlags,int x,int y,CWnd* pWnd,LPCRECT lpRect=NULL);
nFlags:指定菜单在屏幕上显示的位置
x,y:菜单显示位置处的坐标,这里的坐标是屏幕坐标
pWnd:指定拥有菜单的窗口对象
CMenu::GetSubMenu / ::GetSubMenu -获取某个顶层菜单项的下拉菜单
方法一:利用VC提供的组件来创建
1)VC菜单栏->工程->添加到工程->Components and Controls...
2)在对话框中双击Visual C++ Components目录
3)选中“Pop-up Menu”组件,单击<Insert>,确认后就可见到添加右键菜单组件设置对话框
4)Add pop-up menu to:添加右键菜单到哪个类;Menu resource ID:右键菜单的资源ID;
5)返回到Visual C++ Components目录下,点击<Close>按钮,完成组件的添加
在资源窗口的Menu下多了右键菜单的资源;在第4步所选类下增加了函数OnContextMenu,此函数即为右键菜单的调用函数;
方法二:手动创建
1)添加菜单资源
在显示右键菜单时顶层菜单项是不显示的,可以设置任意文本
2)给CMenuView类添加WM_RBUTTONDOWN消息响应函数;
3)定义一个CMenu对象,加载菜单项资源,对于右键菜单来说只有一个顶层菜单项,索引位置为0,调用ClientToScreen函数将客户区的坐标转换为屏幕坐标,调用TrackPopupMenu函数显示右键菜单;
void CMenuView::OnRButtonDown(UINT nFlags, CPoint point) { // TODO: Add your message handler code here and/or call default CMenu menu; menu.LoadMenu(IDR_MENU1); CMenu* pPopup=menu.GetSubMenu(0); ClientToScreen(&point); pPopup->TrackPopupMenu(TPM_LEFTALIGN|TPM_RIGHTBUTTON,point.x,point.y,this); CView::OnRButtonDown(nFlags, point); }
4)利用MFC类向导为右键菜单项的各选项添加消息响应函数
5.7对菜单进行动态操作
1)创建菜单项
1--在框架类中创建菜单项
可在其OnCreate函数中利用:CreateMenu() / CreatePopupMenu()
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) { ...... CMenu menu; menu.CreateMenu(); GetMenu()->AppendMenu(......); menu.Detach(); return 0; }
2--在视图类等其它类的消息响应函数中创建菜单项,须先在该类中添加成员变量:CMenu m_menu
int CTestView::OnChar(UINT nChar,UINT nRepCnt,UINT nFlags) { ...... m_menu.CreatePopupMenu(); GetParent()->GetMenu()->AppendMenu(......); GetParent()->DrawMenuBar(); //操作完成后须重绘菜单栏 return 0; }
2)追加菜单项
利用AppendMenu函数把一个新的菜单项追加到已有菜单项的末尾
BOOL AppendMenu(UINT nFlags,UINT_PTR nIDNewItem=0,LPCTSTR lpszNewItem=NULL);
或:BOOL AppendMenu( UINT nFlags, UINT nIDNewItem, const CBitmap* pBmp );
nFlags:
MF_POPUP/MF_STRING/MF_SEPARATOR
MF_CHECKED/MF_UNCHECKED/MF_ENABLED/MF_DISABLED/MF_GRAYED
MF_OWNERDRAW/MF_MENUBREAK/MF_MENUBARBREAK
nIDNewItem:如果第一个参数为MF_POPUP,则nIDNewItem就是m_hMenu;否则就为所追加的菜单项的ID;为MF_SEPARATOR时直接忽略
lpszNewItem:如果第一个参数为MF_STRING,则为指向要添加的菜单项的文本的指针;第一个参数为MF_OWNERDRAW,则为指向要添加的菜单项的附加数据的指针;其它则直接忽略;
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) { ...... CMenu menu; menu.CreateMenu(); GetMenu()->AppendMenu(MF_POPUP,(UINT)menu.m_hMenu,"Test"); menu.Detach(); return 0; }
3)插入菜单项
利用InsertMenu函数在已有菜单项之前插入一个新的菜单项;
BOOL InsertMenu( UINT nPosition, UINT nFlags, UINT nIDNewItem = 0, LPCTSTR lpszNewItem = NULL );
或:BOOL InsertMenu( UINT nPosition, UINT nFlags, UINT nIDNewItem, const CBitmap* pBmp );
nPosition:指定新菜单项的插入位置;取决于第二个参数nFlags为MF_BYCOMMAND还是WM_BYPOSITION;
nFlags:和AppendMenu中的参数一样 | MF_BYCOMMAND/MF_BYPOSITION
nIDNewItem、lpszNewItem:均与AppendMenu中的参数一样
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) { ...... CMenu menu; menu.CreateMenu(); //在菜单栏的第二个位置处插入新的顶层菜单项 GetMenu()->InsertMenu(1,MF_POPUP|MF_BYPOSITION,(UINT)menu.m_hMenu,"Test"); //在新插入的顶层菜单项下添加下拉菜单项 menu.AppendMenu(MF_STRING,(UINT)IDR_MENU1,"Hello"); menu.AppendMenu(MF_STRING,(UINT)IDR_MENU2,"VC++"); menu.AppendMenu(MF_STRING,(UINT)IDR_MENU3,"MFC"); menu.Detach(); //在菜单栏首个顶层菜单项下追加下拉菜单项 GetMenu()->GetSubMenu(0)->AppendMenu(MF_STRING,(UINT)IDR_MENU1,"Welcom"); //在菜单栏首个顶层菜单项下插入下拉菜单项 GetMenu()->GetSubMenu(0)->InsertMenu(0,MF_BYPOSITION|MF_STRING,(UINT)IDR_MENU5,"Autumn"); return 0; }
4)删除菜单项
BOOL DeleteMenu(UINT nPosition,UINT nFlags);
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) { ...... //删除顶层菜单项 GetMenu()->DeleteMenu(1,MF_BYPOSITION); //删除下拉菜单项 GetMenu()->GetSubMenu(0)->DeleteMenu(0,MF_BYPOSITION); return 0; }
5)动态添加的菜单项的响应命令
动态添加的菜单项未在资源窗口中添加相应的菜单项资源,故没有相应的菜单项ID标识;
1--首先为动态添加的菜单项创建菜单资源ID,在文件窗口中打开Header Files目录,在Resource.h文件中定义资源ID
例:#define IDM_HELLO 111
2--动态追加菜单项AppendMenu函数和动态插入菜单项InsertMenu函数的UINT nIDNewItem参数即为菜单项资源ID(IDM_HELLO)
3--在菜单项命令所在的类(框架类)的头文件中添加命令响应函数的原型
class CMainFrame : public CFrameWnd { ...... // Generated message map functions protected: //{{AFX_MSG(CMainFrame) afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct); // NOTE - the ClassWizard will add and remove member functions here. // DO NOT EDIT what you see in these blocks of generated code! //}}AFX_MSG afx_msg void OnHello(); DECLARE_MESSAGE_MAP() };
4--在菜单项命令所在的类(框架类)的源文件中的映射表添加消息映射
BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd) //{{AFX_MSG_MAP(CMainFrame) // NOTE - the ClassWizard will add and remove mapping macros here. // DO NOT EDIT what you see in these blocks of generated code ! ON_WM_CREATE() //}}AFX_MSG_MAP ON_COMMAND(IDM_HELLO,OnHello) //注意这里结尾没有分号 END_MESSAGE_MAP()
5--在菜单项命令所在的类(框架类)的源文件中添加消息响应函数的定义
void CMainFrame::OnHello() { MessageBox("Hello"); }