MFC技术积累——基于MFC对话框类的那些事儿5
4. 菜单
4.1 弹出菜单
本节主要讲解如何在主对话框的指定区域内通过鼠标右击来弹出一个菜单选项。最终效果图如图4.1。
如图4.1鼠标只能在指定区域(图中深色区域)内右击时弹出菜单,在指定区域外点击时不执行创建菜单操作。具体操作步骤如下:
(1) 首先在指定区域响应鼠标右击消息,需要在主对话框类中添加消息句柄WM_RBUTTONUP,并在该消息响应函数void CFDlg::OnRButtonUp(UINT nFlags, CPoint point)中进行坐标判断,示例代码如下:
void CFDlg::OnRButtonUp(UINT nFlags, CPoint point) { // TODO: Add your message handler code here and/or call default RECT myrc;//指定窗口区域 RECT wndrc;//主对话框窗口区域 RECT myrc_to_wndrc;//指定区域相对主窗口坐标 int pt_to_wndrcX;//光标X位置相对主窗口坐标 int pt_to_wndrcY;//光标Y位置相对主窗口坐标 CPoint pt_to_screen(point);//建立一个相对屏幕坐标系下的光标位置坐标 //获取指定区域的窗口指针 CWnd* mywnd = GetDlgItem(IDC_BITMAPAREA); //获取指定窗口的客户坐标 mywnd->GetClientRect(&myrc); //指定区域相对屏幕坐标系下的坐标 mywnd->ClientToScreen(&myrc); //获取主窗口的客户区域坐标 GetClientRect(&wndrc); //主窗口的客户区域相对屏幕坐标系下的坐标 ClientToScreen(&wndrc); //光标位置相对屏幕坐标下的坐标 ClientToScreen(&pt_to_screen); //计算指定窗口区域和光标位置相对主窗口客户区域的坐标 myrc_to_wndrc.left = myrc.left - wndrc.left; myrc_to_wndrc.right = myrc.right - wndrc.left; myrc_to_wndrc.top = myrc.top - wndrc.top; myrc_to_wndrc.bottom = myrc.bottom - wndrc.top; pt_to_wndrcX = pt_to_screen.x - wndrc.left; pt_to_wndrcY = pt_to_screen.y - wndrc.top; if ( pt_to_wndrcX > myrc_to_wndrc.left && pt_to_wndrcX < myrc_to_wndrc.right && pt_to_wndrcY > myrc_to_wndrc.top && pt_to_wndrcY < myrc_to_wndrc.bottom) { //在指定区域进行鼠标右击消息响应操作,本节是创建弹出式菜单 } CDialog::OnRButtonUp(nFlags, point); }
(2) 其次创建弹出式菜单,示例代码如下:
CMenu mymenu; if (mymenu.CreatePopupMenu()) { //定义两条菜单项显示的字符串,其中‘&’比较有用,用于对弹出菜单通过指定键盘字符来响应 CString strFSR("第 1 条(&F)"); CString strWH("第 2 条(&S)"); //增加第1条菜单项,函数的第二个参数在Resource.h文件已经定义: // #define IDM_FSRMENU 0x0020 // #define IDM_WHMENU 0x0030 mymenu.AppendMenu(MF_STRING, IDM_FSRMENU, strFSR); //添加一条分割线 mymenu.AppendMenu(MF_SEPARATOR); mymenu.AppendMenu(MF_STRING, IDM_WHMENU, strWH ); //在指定位置显示一个弹出式菜单 mymenu.TrackPopupMenu(TPM_LEFTALIGN | TPM_LEFTBUTTON, pt_to_screen.x, pt_to_screen.y,this); }
(3) 查看MSDN对CMenu::TrackPopupMenu的解释可知,TrackPopupMenu的第4个参数表示哪个窗口拥有这个菜单,该窗口处理菜单的WM_COMMAND消息。因此还需要添加对菜单条目的响应处理,示例代码如下:
BOOL CFDlg::OnCommand(WPARAM wParam, LPARAM lParam) { //添加菜单条目的命令消息代码 if (wParam = = IDM_FSRMENU) { MessageBox("选择第 1 条菜单选项"); } else if (wParam = = IDM_WHMENU) { MessageBox("选择第 2 条菜单选项"); } else { CDialog::OnCommand(wParam,lParam); } return TRUE; }
此外还需要对OnCommand函数进行申明:
virtual BOOL OnCommand( WPARAM wParam, LPARAM lParam );系统框架调用该函数。
最后,结合图文方式介绍几个菜单条目操作:
(1) 菜单条目前添加位图,示例代码如下:
CString strFSR("第 1 条(&F)"); CString strWH("第 2 条(&S)"); // 增加第1条菜单条目,函数的第一个参数选择了MF_CHECKED,表示该菜单条目前有一个选中位图 //(形如“√”),如图4.2的第1条菜单条目所示 mymenu.AppendMenu(MF_STRING | MF_CHECKED , IDM_FSRMENU, strFSR ); mymenu.AppendMenu(MF_SEPARATOR);//添加分割线 mymenu.AppendMenu(MF_STRING, IDM_WHMENU, strWH );//添加第2条菜单条目 //添加第3条菜单条目,函数的第1个参数选择了MF_CHECKED mymenu.AppendMenu(MF_CHECKED, 0x0040,"第 3 条(&T)"); // 为第3条菜单条目前增加一个位图,函数的第3、4参数分别表示当菜单未选中时使用的位图和菜单选
// 中时所用的位图。因此首先之前已经创建了两幅CBitmap对象。如图4.2所示,第3条菜单的状态是选 // 中,因此前面的“√”消失,取而代之的是一幅位图。而当第3条菜单条目是未被选中(AppendMenu // 第1个参数是MF_UNCHECKED)那么此时该条目前显示的是未选中状态的位图,如图4.3。 mymenu.SetMenuItemBitmaps(0x0040,MF_BYCOMMAND,&uncheck_map,&check_map);
mymenu.AppendMenu(MF_STRING, 0x0050,"第 4 条(&D)");//添加第4条菜单条目
实现效果如图4.2和4.3:
(2) 创建层叠菜单,效果图如图4.4:
示例代码如下:
CMenu mymenu; CMenu newmenu;//-- //创建一个层叠菜单,在后续操作时将其关联到主菜单的指定条目上 newmenu.CreatePopupMenu(); newmenu.AppendMenu(MF_UNCHECKED,0x0060,"层叠1"); newmenu.AppendMenu(MF_CHECKED,0x0070,"层叠2"); newmenu.AppendMenu(MF_STRING,0x0080,"层叠3"); CBitmap check_map; CBitmap uncheck_map; if (mymenu.CreatePopupMenu()) { check_map.LoadBitmap(IDB_BITMAP3); uncheck_map.LoadBitmap(IDB_BITMAP4); CString strFSR("第 1 条(&F)"); CString strWH("第 2 条(&S)"); mymenu.AppendMenu(MF_STRING | MF_CHECKED , IDM_FSRMENU, strFSR ); mymenu.AppendMenu(MF_SEPARATOR); //创建层叠菜单时,AppendMenu的第2个参数是层叠菜单的句柄,第1个参数为MF_POPUP mymenu.AppendMenu(MF_POPUP, (UINT)newmenu.m_hMenu, strWH ); mymenu.AppendMenu(MF_CHECKED, 0x0040,"第 3 条(&T)"); mymenu.SetMenuItemBitmaps(0x0040,MF_BYCOMMAND,&uncheck_map,&check_map); mymenu.AppendMenu(MF_STRING, 0x0050,"第 4 条(&D)"); mymenu.TrackPopupMenu(TPM_LEFTALIGN | TPM_LEFTBUTTON, pt_to_screen.x, pt_to_screen.y, this); }
4.2 窗口菜单
最后介绍一下如何在对话框窗口创建菜单,如图4.5所示:
方法很简单,只需要在BOOL CFDlg::OnInitDialog()函数中添加如下代码:
CMenu mymenu;
mymenu.LoadMenu(IDR_MENU1);
SetMenu(&mymenu);
mymenu.DestroyMenu();
其中ID号IDR_MENU1是在资源中创建一个菜单资源如图4.6所示:
最后对每个菜单选项进行ID号编辑后,即可在OnCommand函数中进行消息响应操作。