五、 右键弹出菜单
1, Project->Add to Project->Components and Controls添加pop menu即可。
到哪一个类中?注意不要选择MainFrame中,而要加到View中!!
运行,增加了Popup菜单
程序中增加了OnContextMenu函数
You can process this message by displaying a context menu using the TrackPopupMenu.
Displays a floating pop-up menu at the specified location and tracks the selection of items on the pop-up menu.
BOOL TrackPopupMenu(
UINT nFlags,//在鼠标右下方显示?上方显示?
int x,
int y,
CWnd* pWnd,//拥有此弹出菜单的窗口
LPCRECT lpRect = 0
lpRect
Points to a RECT structure or CRect object that contains the screen coordinates of a rectangle within which the user can click without dismissing the pop-up menu. If this parameter is 0, the pop-up menu is dismissed if the user clicks outside the pop-up menu. This must be 0 for Windows 3.0. //如果在区域菜单外点击,则弹出菜单消失
);
2, 静态添加菜单方法。
1) 在资源里编辑一个菜单
2) View中添加WM_RBUTTONDOWN消息对应函数。
3) 在OnRButtonDown中添加如下
CMenu menu;
menu.LoadMenu(IDR_MENU1);
CMenu *pPopup=menu.GetSubMenu(0);
ClientToScreen(&point);//客户区到屏幕坐标转换,实际编程中最恨不知道这种事
pPopup->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, point.x, point.y,GetParent());//不想在框架类显示就把GetParent()改为this。
//class wizard提示要不要为新的菜单资源增加类,不用管它
//在View和MainFrame中分别加入OnShow响应
//View show先于MainFrame show!
//去掉View show,发现MainFrame show还是不出来(因为此时在上面用的是this指针,只能由view类来响应)
//改成GetParent()以后,发现MainFrame show终于出现了
//此时再加入View show,发现又是View show出来!
//子窗口享有优先响应机会,如果子窗口没有机会,则才能交给父窗口
4) 给自己编辑的菜单加对应的处理函数(利用Classwizard)。
如果加在CMainFrame
5)
3, 动态添加菜单(子菜单数目变化)
在MainFrame::OnCreate中试验如下
CMenu menu;
menu.CreatePopupMenu();
GetMenu()->AppendMenu(MF_POPUP,(UINT)menu.m_hMenu,"WinSun");
//如果第一个参数是MF_POPUP,则第二个参数是句柄
//因是局部对象,注意要Detach
BOOL AppendMenu(
HMENU hMenu, UINT uFlags, UINT_PTR uIDNewItem, LPCTSTR lpNewItem );
uFlags: MF_SEPARATOR
CMenu::CreatePopupMenu函数-->MSDN
GetMenu()->InsertMenu(2,MF_BYPOSITION | MF_POPUP,(UINT)menu.m_hMenu,"WinSun");
BOOL InsertMenu( HMENU hMenu, UINT uPosition, UINT uFlags, PTR uIDNewItem, LPCTSTR lpNewItem
); menu.AppendMenu(MF_STRING,IDM_HELLO,"Hello");
menu.AppendMenu(MF_STRING,112,"Weixin");
menu.AppendMenu(MF_STRING,113,"Mybole");
menu.Detach();
响应函数添加方法:
1) 在resource.h中添加资源ID定义
#define IDM_HELLO 111
2) 在MainFrm.h中声明消息响应。DECLARE_MESSAGE_MAP()之前添加
afx_msg void OnHello();
3) 在MainFrm.cpp中END_MESSAGE_MAP()之前,添加
ON_COMMAND(IDM_HELLO,OnHello)
//注意不要加在默认的那一坨中间,否则系统有可能神不知鬼不觉地干掉它
4) 在MainFrm.cpp中添加CMainFrame::OnHello()函数定义
//小贴士:先添一个废的,模仿之;再删之 mock...
4, 给系统菜单添加/删除菜单项
GetMenu()->GetSubMenu(0)->AppendMenu(MF_STRING,114,"Welcome");
GetMenu()->GetSubMenu(0)->InsertMenu(ID_FILE_OPEN,
MF_BYCOMMAND | MF_STRING,115,"维新");//在"打开"之前加入
GetMenu()->DeleteMenu(1,MF_BYPOSITION);
GetMenu()->GetSubMenu(0)->DeleteMenu(2,MF_BYPOSITION);*/
GetMenu()->DeleteMenu( GetMenu()->GetSubMenu(1)->GetMenuItemID(0),MF_BYCOMMAND);
六、 动态添加系统菜单项
电话本程序:键盘输入人名,空格之后电话号码。回车之后,把人名添加到菜单项。当点击菜单时,显示电话本信息。
1, 给View添加private 的
CString m_strLine;//存储输入的字符串
CMenu m_menu;//菜单
int m_nIndex=-1;//用于计数,是不是第一次
2, 添加View的public成员
CStringArray m_strArray;
3, 添加View的WM_CHAR消息响应函数
4, 在View::OnChar中添加如下代码
CClientDC dc(this);
if(0x0d==nChar)
{
if(0==++m_nIndex)//如果是第一次,就创建菜单
{
m_menu.CreatePopupMenu();
GetParent()->GetMenu()->AppendMenu(MF_POPUP,
(UINT)m_menu.m_hMenu,"PhoneBook");
GetParent()->DrawMenuBar();//立即显示菜单
//这里要注意GetParent(),因View类没有menubar,会再现问题
}
m_menu.AppendMenu(MF_STRING,IDM_PHONE1+m_nIndex,m_strLine.Left(m_strLine.Find(' ')));//Common技俩,要学会用...
//下面的技巧也比较好用...
#define IDM_PHONE1 201
#define IDM_PHONE2 202
#define IDM_PHONE3 203
//注意不要加在默认的那一坨中间,否则系统有可能神不知鬼不觉地干掉它
//所以要将ON_COMMAND(IDM_PHONE1, OnPhone1)等四个拿到外面来
//在里面只不过在class wizard里可以看到而已.
m_strArray.Add(m_strLine);
m_strLine.Empty();
Invalidate();//擦除先前的背景,防止重叠显示
}
else
{
m_strLine+=nChar;
dc.TextOut(0,0,m_strLine);
}
5, 添加菜单的响应函数
方法一:
1) 在Toolbar上添加一个菜单,其弹出项为ID号为IDM_PHONE1, IDM_PHONE2, IDM_PHONE3。
2) 在Rsource.h中添加
#define IDM_PHONE1 201
#define IDM_PHONE2 202
#define IDM_PHONE3 203
3) 利用ClassWizard给菜单项添加响应函数,并编辑函数
4) 删除菜单项(其响应函数会依然存在,并且4步中利用了ID号,可以对应过去)
方法二:
视频没有提供,但我感觉应该有其他方法。方法一太死板
//同意,确实比较傻
//就是消息映射不能"variable",比如说我从数据库读数据,再加入菜单,事先不晓得有几项,怎么办呢?
//MFC在这里就傻了...
6, 另:由框架类捕获菜单响应,而不是由View处理,可以节约代码
1) 给MainFrame添加WM_COMMAND消息响应函数, (注意是增加virtual function)
2) 在其中添加如下代码 (首先截获一下菜单栏消息) (只对动态添加的菜单项感兴趣)
int MenuCmdId=LOWORD(wParam);
The low-order word of wParam identifies the command ID of the menu item, control, or accelerator. The high-order word of wParam specifies the notification message if the message is from a control. If the message is from an accelerator, the high-order word is 1. If the message is from a menu, the high-order word is 0.
CMenu2View *pView=(CMenu2View*)GetActiveView();//要在头文件里面加
//"Menu2View"-->类名上双击,或File View
//如果不知道GetActiveView();这个函数,则应去查找CMainFrame成员函数!!这是习惯
if(MenuCmdId>=IDM_PHONE1 &&MenuCmdId<IDM_PHONE1+pView->m_strArray.GetSize())
{
CClientDC dc(pView);//牛
dc.TextOut(0,0,pView->m_strArray.GetAt(MenuCmdId-IDM_PHONE1));
//如果MenuCmdId为IDM_PHONE1,则只取0个...
//...还比较巧
return TRUE;
}
第6点提供的方法,不能解决第5点提出方法的限制,响应菜单还是和事先编好的ID相对应,不能动态增加
OnCommand
OnCommand processes the message map for control notification and ON_COMMAND entries, and calls the appropriate member function.
思路: 如果我们在框架类中覆盖OnCommand函数,就可以截获这个函数,让其不再往下路由
RECALL:
AfxWndProc-->AfxCallWndProc-->WindowProc-->OnWndMsg-->
-->1 OnCommand
-->2 OnNotify
-->OnCmdMsg
其他注意的地方:
1, 弹出菜单(Pop-up)是不能用来作命令响应的。此项设置在菜单属性栏内。
2, 将Toolbar的ID设为菜单的ID,二者即发生了关联。
3, 在计算子菜单菜单项的索引的时候,分隔栏符也算索引。
4, 消息影射函数
Cpp文件中
BEGIN_MESSAGE_MAP(CMenu2View, CView)
//{{AFX_MSG_MAP(CMenu2View)
//位置1
//}}AFX_MSG_MAP
位置2
END_MESSAGE_MAP()
头文件中
//{{AFX_MSG(CMenu2View)
位置3
//}}AFX_MSG
位置4
DECLARE_MESSAGE_MAP()
消息映射在位置1和位置2不一样,可以调用Classwizard看到修改结果
位置3和位置4的函数声明,随便放1或2都可以,没有影响