本文目的:菜单命令的路由、菜单的的基本操作、右键菜单的创建及动态菜单的创建。
一、Windows消息的分类
1、标准消息:除去WM_COMMAND之外的所有以WM_开头的消息都是标准消息。从CWnd派生的类可以接受到这类消息。
2、命令消息:来自菜单、加速键或者工具栏按钮的消息。这类消息以WM_COMMAND形式呈现。在MFC中,通过菜单项的标识(ID)来区分不同的命令消息;在SDK中,通过wParam参数识别。从CCmdTarget派生的类都可以接收到这类消息。(注:CWnd也是派生自CCmdTarget类的)
3、通告消息:由控件产生的消息,目的是向父窗口通知事件的发生。其形式与接收类与命令消息相同。
二、菜单命令的路由
程序对菜单命令响应的顺序为:视图类->文档类->框架类->应用程序类。
具体路由顺序为:框架类接收到菜单命令消息后,将消息交给它的子窗口即视图类;视图类首先对消息进行处理,若响应了,则进行该消息的处理,消息路由结束,若消息映射机制检查没有响应该消息的响应函数,则将消息交给文档类;同样,文档类判断是否对该消息进行了响应,若没有 它会将这个消息交还给视图类,视图类又交给框架类;这个时候框架类再查看自己是否对这个消息进行了响应,若没有,则将这个菜单消息交给应用程序类进行处理。
三、基本菜单操作
①CMenu * GetMenu() const 获取指向菜单栏的指针,CMenu类是一个MFC类,提供了一些与菜单操作相关的成员函数。
②CMenu * GetsubMenu(int nPos) const 获取一个菜单的子菜单,nPos为子菜单的索引号,②返回的CMenu * 与①返回的CMenu* 所指向的对象是不同的。
③UINT CheckMenuItem(UINT nIDCheckItem, UINT nCheck) ,该函数具体用法参见MSDN。
④BOOL SetDefaultItem(UINT uItem, BOOL fByPos = FALSE) 将该子菜单的一个菜单项设置为默认菜单项(粗体显示)。
③和④两个函数的第一个参数均有第二个参数决定。
⑤SetMenuItemBitmaps(...) 设置图形标记菜单。
⑥int GetSystemMetrics(int nIndex) 可以得到图形标记菜单上显示的位图的尺寸。从而我们可以在资源视图中对菜单进行响应的调整。
⑦UINT EnableMenuItem(UINT nIDEnableItem, UINT nEnable) 设置菜单项的状态:能够使用、禁用、变灰等。要想该函数能正常工作,需要在CMainFrame 类中的构造函数中将成员变量m_bAutoMenuEnable设置为FALSE,这是MFC提供的一种命令更新机制,该机制去判断各个菜单的状态。设置为FALSE后,更新将由我们自己完成。
⑧SetMenu(CMenu * pMenu) 移除菜单,CMenu类中的LoadMenu()函数加载菜单。
⑨CMenu类中的Detach()函数将菜单句柄与这个菜单对象分离。
四、右键菜单
又称快捷菜单或上下菜单。
方法一:通过添加组件的方式添加Popup菜单组件。
方法二:手动添加
1、 在资源视图中创建要显示的右键菜单;
2、为视图类添加鼠标右键点下的消息响应函数,在该函数里进行处理。CMenu menu; menu.LoadMenu(IDR_MENU1); CMenu * pPpoup = menu.GetSubMenu(0);
ClientToScreen(&point); pPopup->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, point.x,point.y,this);
五、动态菜单
即在程序运行过程中创建菜单。一种是针对弹出菜单的动态操作,一种是针对菜单项的动态操作。
⑩BOOL AppendMenu(UINT nFlags, UINT_PTR nIDNewItem = 0, LPCTSTR lpszNewItem = NULL);把一个菜单项添加到一个制定菜单项目的末尾。
相关函数有: CreateMenu(),InsertMenu(),DeleteMenu()。
关于OnPhone的消息响应函数,我们可以先在资源视图中相应位置建立菜单,并生成消息响应函数,注意将BEGIN_MESSAGE_MAP 下的OnPhone 映射拷贝出注释宏,然后删掉资源视图中我们刚建立的菜单,但OnPhone的消息响应保留了下来,这为我们下面的操作提供了很大便利。
最后贴上一段电话本示例OnChar代码
void CChapter6_2View::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
{
// TODO: Add your message handler code here and/or call default
CClientDC dc(this);
if(13 == nChar)
{
if( 0 == ++m_nIndex)
{
m_menu.CreatePopupMenu();
GetParent()->GetMenu()->AppendMenu(MF_POPUP,
(UINT)m_menu.m_hMenu,"PhoneBook");
GetParent()->DrawMenuBar();
}
m_menu.AppendMenu(MF_STRING,IDM_PHONE1+m_nIndex,
m_str.Left(m_str.Find(' ')));
m_strArray.Add(m_str);
m_str.Empty();
Invalidate();
}
else
{
m_str += nChar;
dc.TextOut(0,0,m_str);
}
CView::OnChar(nChar, nRepCnt, nFlags);
}
注意在响应的OnPhone 函数中添加以下代码
CClientDC dc(this);
Invalidate();<span style="white-space:pre"> </span>//刷新显示的数据
UpdateWindow();
dc.TextOut(0,0,m_strArray.GetAt(0)); //索引号可以改变
除此我们还可以通过框架类来截获菜单命令消息,通过重写OnCommand()函数来实现,它是个虚函数,你懂的。
BOOL CMainFrame::OnCommand(WPARAM wParam, LPARAM lParam)
{
// TODO: Add your specialized code here and/or call the base class
int MenuCmdId = LOWORD(wParam);
CChapter6_2View * pView = (CChapter6_2View*)GetActiveView();
if(MenuCmdId >= IDM_PHONE1 && MenuCmdId < IDM_PHONE1
+ pView->m_strArray.GetSize())
{
Invalidate();
UpdateWindow();
//MessageBox("test");
CClientDC dc(pView);
dc.TextOut(0,0,pView->m_strArray.GetAt(MenuCmdId-IDM_PHONE1));
return TRUE;
}
return CFrameWnd::OnCommand(wParam, lParam);
}