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)移除菜单栏
方法一:适用于VC6
在CMainFrame类的OnCreate函数的最后添加——“this->SetMenu(NULL);”
或者:在CMainFrame类的PreCreateWindow函数的最后添加——“cs.hMenu=NULL;”
方法二:适用于VS2010
CMainFrame类的OnCreate函数中:
1.注释掉
2.CMainFrame类的PreCreateWindow函数中添加:
3.CMainFrame类的头文件中注释掉:
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");
}
5.8改变菜单背景色、添加图标
CMenu mMenuConfig;
CBrush mMenuBrush;
if (!mMenuConfig.m_hMenu)
{
mMenuConfig.CreatePopupMenu();
mMenuConfig.AppendMenu(MF_STRING,ID_MENU_MORE_CONFIG, _T("设置"));
mMenuConfig.AppendMenu(MF_STRING,ID_MENU_MORE_UPGRADE, _T("更新"));
mMenuConfig.AppendMenu(MF_STRING,ID_MENU_MORE_ABOUT, _T("关于"));
mMenuBrush.CreateSolidBrush(RGB(240,240,0));//RGB(255,128,128));
MENUINFO mi;
mi.cbSize=sizeof(MENUINFO);
mi.fMask=MIM_BACKGROUND;
mi.hbrBack=(HBRUSH)mMenuBrush;
SetMenuInfo((HMENU)(mMenuConfig.m_hMenu),&mi);
CBitmap bmp;
bmp.LoadBitmap(IDB_BITMAP_CONFIG);//IDB_BITMAP_CONFIG为导入工程中的bmp图片的id;
mMenuConfig.SetMenuItemBitmaps(ID_MENU_MORE_CONFIG, MF_BYCOMMAND, bmp, bmp);//此处常态和高亮显示为一样;
bmp.Detach();//必须Detach,否则图标不显示
DeleteObject(mMenuBrush);
}