孙鑫VC++第06课:菜单编程02——弹出菜单与动态创建菜单
3、右键弹出菜单
3.1使用自动向导创建ContentMenu
工程->添加到工程->组件与控件->Visual C++组件->Pop-up Menu
增加到View类中。(不能选择框架类,框架类响应不到鼠标右键消息)
完成后运行程序,右键点击后,出现弹出菜单。
添加Pop-up Menu后,新增加了一个菜单资源。在View类中增加了一个OnContentMenu函数。
afx_msg void CWnd::OnContentMenu(CWnd * pWnd, CPoint pos);//用户点击右键时,该函数被框架调用。可以通过TrackPopupMenu函数显示弹出上下文菜单来处理这个消息
BOOL CMenu::TrackPopupMenu(UINT nFlags, int x, int y, CWnd* pWnd, LPCRECT lpRect=NULL);//弹出菜单
nFlags:弹出菜单显示的位置,鼠标相对的位置
x,y:鼠标位置(相对于屏幕的鼠标位置)
pWnd:标识了弹出菜单的拥有者(窗口)
lpRect:矩形区域。区域外点击鼠标,弹出菜单消失,用于定义这个区域。
3.2手动创建右键菜单
3.2.1 新建菜单。
在资源中新建一个菜单,添加两个子菜单:IDM_SHOW显示,IDM_EXIT退出
3.2.2 捕获鼠标右键消息
在View类中处理鼠标右键消息。增加windows消息处理->LBUTTONDOWN
编写代码过程中,可以参考自动生成的右键菜单代码:
1 void CSunXin06MenuView::OnRButtonDown(UINT nFlags, CPoint point) 2 { 3 // TODO: Add your message handler code here and/or call default 4 CMenu menu; 5 menu.LoadMenu(IDR_MENU1); 6 CMenu *pPopup = menu.GetSubMenu(0); 7 pPopup->TrackPopupMenu(TPM_LEFTALIGN|TPM_RIGHTALIGN, point.x, point.y, this); 8 CView::OnRButtonDown(nFlags, point); 9 }
运行后发现鼠标右键点击后,弹出菜单并没有在鼠标键头位置弹出。因为TrackPopupMenu显示菜单时,以屏幕为坐标,相对于屏幕左上偏移point坐标显示。
需要将屏幕坐标与客户区坐标进行转换。将客户区坐标转换成屏幕坐标,传给TrackPopupMenu。
CWnd成员函数void CWnd::ClientToScreen(LPPOINT lpPoint) const;//将客户区坐标到屏幕坐标的转换
1 void CSunXin06MenuView::OnRButtonDown(UINT nFlags, CPoint point) 2 { 3 // TODO: Add your message handler code here and/or call default 4 CMenu menu; 5 menu.LoadMenu(IDR_MENU1); 6 CMenu *pPopup = menu.GetSubMenu(0); 7 ClientToScreen(&point);//将客户区鼠标点击的坐标转换成屏幕坐标 8 pPopup->TrackPopupMenu(TPM_LEFTALIGN|TPM_RIGHTBUTTON, point.x, point.y, this); 9 CView::OnRButtonDown(nFlags, point); 10 }
3.2.3弹出菜单点击命令捕获
在CMainFrame类和View类中均增加一个COMMAND命令响应函数。分别使用MessageBox尝试弹出窗口。
运行程序后,View类弹窗。把View类中的响应函数删除后,再次运行,CMainFrame的并没有调用命令响应函数。
在设计弹出菜单的拥有者时,传入的是this,即View类对象。因此,只有由拥有者弹出右键菜单并进行命令响应。如果希望框架类能响应右键菜单的命令,则需要在TrackPopupMenu中最后一个参数传入框架类的指针。
pPopup->TrackPopupMenu(TPM_LEFTALIGN|TPM_RIGHTBUTTON, point.x, point.y, GetParent());
此时再向View类中添加菜单的命令响应,运行后,View类弹窗。与之前test菜单的命令响应顺序一样,子窗口拥有优先响应的顺序。如果子窗口未响应,再交由其父窗口继续响应(前提是TrackPopupMenu中指定父窗口是其拥有者)
4、动态添加、删除菜单
4.1 添加子菜单(弹出菜单)
在CMainFrame框架窗口创建完成后,操作菜单。
创建一个新的MFC工程。
BOOL CMenu::AppendMenu(UINT nFlags, UINT nIDNewItem = 0, LPCTSTR lpszNewItem = NULL);//将一个子?菜单或菜单项添加到现有菜单的末尾
BOOL CMenu::AppendMenu(UINT nFlags, UINT nIDNewItem, const CBitmap* pBmp);
nFlags:菜单的类型
nIDNewItem:菜单的ID。如果nFlags设置成了MF_POPUP(弹出菜单没有ID),该参数将设置成菜单的句柄。如果nFlags是MF_SEPARATOR分割条,则本参数忽略
lpszNewItem:常量字符串,指示菜单显示的名称
在添加新的弹出菜单前,需要先创建弹出菜单,通过CMenu的成员函数CreatePopupMenu进行创建
BOOL CMenu::CreatePopupMenu();//创建一个空的弹出菜单,将其attach到CMenu对象上。可以认为是将Menu资源与CMenu对象相关联
通过menu对象获取Menu的句柄(所有资源相关的类,内部都有一个成员变量,保存了与对象相关联的资源的句柄)CMenu::m_hMenu
CMenu menu;//创建一个menu menu.CreatePopupMenu();//将menu对象与资源关联起来 GetMenu()->AppendMenu(MF_POPUP, (UINT)menu.m_hMenu, _T("新建菜单"));
运行程序后,窗口菜单栏能正常显示刚刚添加的弹出菜单。但点击后弹窗提示错误,因为新建的菜单是成员变量。调用menu的detach即可。
4.2 插入子菜单/菜单项
BOOL CMenu::InsertMenu(UINT nPosition, UINT nFlags, UINT nIDNewItem= 0, LPCTSTR lpszNewItem = NULL);
BOOL CMenu::InsertMenu(UINT nPosition, UINT nFlags, UINT nIDNewItem, const CBitmap* pBmp);
nFlags:如果插入的flag是Popup,nIDNewItem的值将是HMENU;如果插入的flag是separator,nIDNewItem将被忽略
CMenu menu;//创建一个menu menu.CreatePopupMenu();//将menu对象与资源关联起来 GetMenu()->InsertMenu(2, MF_BYPOSITION|MF_POPUP, (UINT)menu.m_hMenu, _T("新建菜单")); menu.Detach();
4.2.1 增加菜单项
CMenu menu;//创建一个menu menu.CreatePopupMenu();//将menu对象与资源关联起来 GetMenu()->InsertMenu(2, MF_BYPOSITION|MF_POPUP, (UINT)menu.m_hMenu, _T("新建菜单")); menu.AppendMenu(MF_STRING, 111, _T("菜单项1"));//弹出菜单中增加菜单项 menu.AppendMenu(MF_STRING, 112, _T("菜单项2")); menu.AppendMenu(MF_STRING, 113, _T("菜单项3")); menu.Detach();
在“文件”子菜单下添加菜单项
GetMenu()->GetSubMenu(0)->AppendMenu(MF_STRING, 114, _T("文件增加"));
在“文件”子菜单下,“打开”菜单项前面插入一个菜单项
GetMenu()->GetSubMenu(0)->InsertMenu(ID_FILE_OPEN, MF_BYCOMMAND | MF_STRING, 115, _T("插入菜单项"));//在"文件"-"打开"前面添加菜单项
4.3 删除子菜单/菜单项
删除一个指定菜单项/弹出菜单(depends on调用该函数的对象)
BOOL CMenu::DeleteMenu(UINT nPosition, UINT nFlags);//第一个参数取值由第二个参数决定
GetMenu()->DeleteMenu(1, MF_BYPOSITION);//删除索引位置是1的子菜单(删除Edit)
GetMenu()->GetSubMenu(0)->DeleteMenu(ID_FILE_NEW, MF_BYCOMMAND);//删除File子菜单下的"新建"菜单项
4.4 为动态创建的菜单项增加命令响应
VS的FileView->Header Files->Resource.h添加资源ID
菜单命令响应的函数的添加过程与消息映射过程一样,三个步骤:
1) 头文件中做命令响应函数的原型
afx_msg void OnMenu01();
2) 完成消息映射(命令消息通过ON_COMMAND宏来完成)ON_COMMAND(资源ID, 消息响应函数)后面无标点符号
BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd) ON_WM_CREATE() ON_COMMAND(IDM_MENU_01, OnMenu01)//菜单命令消息映射 END_MESSAGE_MAP()
3) 命令消息响应函数定义
1 void CMainFrame::OnMenu01() 2 { 3 AfxMessageBox(_T("动态创建的菜单响应")); 4 }
浙公网安备 33010602011771号