一、基础知识
标准消息
除WM_COMMAND之外,所有以WM_开头的消息。
从CWnd派生的类,都可以接收到这类消息。
命令消息
来自菜单、加速键或工具栏按钮的消息。这类消息都以WM_COMMAND呈现。在MFC中,通过菜单项的标识(ID)来区分不同的命令消息;在SDK中,通过消息的wParam参数识别。
从CCmdTarget派生的类,都可以接收到这类消息。
通告消息
由控件产生的消息,例如,按钮的单击,列表框的选择等均产生此类消息,为的是向其父窗口(通常是对话框)通知事件的发生。这类消息也是以WM_COMMAND形式呈现。
从CCmdTarget派生的类,都可以接收到这类消息。
从CWND派生的类可接收标准消息和命令消息;从CCmdTarget派生的类可接收命令消息。
单文档应用程序处理消息的过程:
MainFrame接收到消息之后,首先将消息发送给view处理,view如果没有处理,就发送给doc处理,doc不能处理则返回给main处理,main不能处理就发送给app,最终由app处理。
MessageBox是CWND派生类的一个方法,不是CWND类没有这个方法,只能使用AfxMessageBox
为菜单项添加命令响应函数的方法:
在view--classwizard中,object下选择菜单项id,在message下选择command。添加命令响应函数。
二、菜单操作
1、标记菜单
在int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)中、
GetMenu()->GetSubMenu(0)->CheckMenuItem(0,MF_BYPOSITION | MF_CHECKED);
或
GetMenu()->GetSubMenu(0)->CheckMenuItem(ID_FILE_NEW,MF_BYCOMMAND | MF_CHECKED);
2、设置缺省菜单(粗体)
在int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)中如:
GetMenu()->GetSubMenu(0)->SetDefaultItem(1,TRUE);
GetMenu()->GetSubMenu(0)->SetDefaultItem(ID_FILE_OPEN);
GetMenu()->GetSubMenu(0)->SetDefaultItem(5,TRUE);
缺省菜单只能有一项
3、创建图形标记菜单
m_bitmap.LoadBitmap(IDB_BITMAP1);
GetMenu()->GetSubMenu(0)->SetMenuItemBitmaps(0,MF_BYPOSITION,&m_bitmap,&m_bitmap);
4、让菜单项不能使用
首先在mainframe的构造函数中
m_bAutoMenuEnable=FALSE;
然后
GetMenu()->GetSubMenu(0)->EnableMenuItem(1,MF_BYPOSITION | MF_DISABLED | MF_GRAYED);
5、将整个菜单取消
SetMenu(NULL);
6、在5的基础上将菜单重新添加进来(动态添加菜单)
CMenu menu;
menu.LoadMenu(IDR_MAINFRAME);
SetMenu(&menu);
menu.Detach();//将cmenu对象与句柄断开,否则menu对象应该设置成全局对象
三、命令更新机制
在view--classwizard中,object下选择菜单项id,在message下选择UPDATE_COMMAND_UI。添加命令响应函数。
1、
void CMainFrame::OnUpdateEditCut(CCmdUI* pCmdUI)
{
// TODO: Add your command update UI handler code here
if(2==pCmdUI->m_nIndex)
//if(ID_EDIT_CUT==pCmdUI->m_nID)
pCmdUI->Enable();//使得cut使能,这时不需要m_bAutoMenuEnable=FALSE;
}
2、
void CMainFrame::OnUpdateFileNew(CCmdUI* pCmdUI)
{
// TODO: Add your command update UI handler code here
// if(ID_FILE_NEW==pCmdUI->m_nID)
// pCmdUI->Enable(FALSE);
if(0==pCmdUI->m_nIndex)
pCmdUI->Enable(FALSE);//filenew不使能
}
四、右键弹出菜单
1、工程--添加到工程--components and contros--visual c++ components---popup menu
2、view中添加点击右键消息响应函数
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,
GetParent());
CView::OnRButtonDown(nFlags, point);
}
五、动态添加、删除、插入菜单
1、动态添加菜单
在int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)中
1)CMenu menu;
menu.CreatePopupMenu();//创建一个空的弹出菜单
GetMenu()->AppendMenu(MF_POPUP,(UINT)menu.m_hMenu,"WinSun");//将菜单append到现有菜单中
menu.Detach();//将句柄和cmenu对象断开
2)插入菜单
CMenu menu;
menu.CreatePopupMenu();//创建一个空的弹出菜单
GetMenu()->InsertMenu(2,MF_BYPOSITION | MF_POPUP,(UINT)menu.m_hMenu,"WinSun");
menu.Detach();//将句柄和cmenu对象断开
3)插入菜单项
CMenu menu;
menu.CreatePopupMenu();//创建一个空的弹出菜单
GetMenu()->AppendMenu(MF_POPUP,(UINT)menu.m_hMenu,"WinSun");//将菜单append到现有菜单中
menu.AppendMenu(MF_STRING,IDM_HELLO,"Hello");
menu.AppendMenu(MF_STRING,112,"Weixin");
menu.AppendMenu(MF_STRING,113,"Mybole");
menu.Detach();//将句柄和cmenu对象断开
4)删除子菜单
GetMenu()->DeleteMenu(1,MF_BYPOSITION);
GetMenu()->GetSubMenu(0)->DeleteMenu(2,MF_BYPOSITION);
2、动态添加的菜单的命令响应
1)在头文件中为子菜单项添加id号
fileview--headfile--resource.h中
#define IDM_HELLO 111
2)在mainframe中做命令响应函数原型
在头文件中的
protected:
//{{AFX_MSG(CMainFrame)
afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
//}}AFX_MSG
afx_msg void OnHello();//此处为原型声明
DECLARE_MESSAGE_MAP()
3)在源文件中进行消息映射
BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
//{{AFX_MSG_MAP(CMainFrame)
ON_WM_CREATE()
//}}AFX_MSG_MAP
ON_COMMAND(IDM_HELLO,OnHello)//此处
END_MESSAGE_MAP()
4)编写消息响应函数
void CMainFrame::OnHello()
{
MessageBox("Hello!");
}
六、电话本程序
功能描述:
1、在view视窗内输入人名和电话号码,人名和电话号码之间以空格分开。回车后将产生电话本菜单项,每一个菜单项以人名为caption;
2、点击菜单项时,将人名和电话号码显示出来。
实现步骤:
1、在view中增加WM_CHAR消息响应函数
void CMenu2View::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
{
// TODO: Add your message handler code here and/or call default
CClientDC dc(this);//创建dc
if(0x0d==nChar)//如果输入的是回车键
{
if(0==++m_nIndex)//m_index初始值为-1,此处判断是否是第一次回车,如果是则创建弹出菜单,否则添加菜单项
{
m_menu.CreatePopupMenu();//创建空的弹出菜单
GetParent()->GetMenu()->AppendMenu(MF_POPUP,
(UINT)m_menu.m_hMenu,"PhoneBook");//将弹出菜单append到现有菜单
GetParent()->DrawMenuBar();//使菜单发生重绘操作,立即显示
}
//下面已经不是第一次回车了,所以append的是菜单项。第一个参数是表示产生菜单项,第二个参数是表示菜单项的索引值,第三项表示返回空格左边的字符作为菜单项的caption
//注:m_strLine保存的是“用户名 电话号码”,在fileview--resource.h中有如下定义:
#define IDM_PHONE1 201
#define IDM_PHONE2 202
#define IDM_PHONE3 203
#define IDM_PHONE4 204
///
m_menu.AppendMenu(MF_STRING,IDM_PHONE1+m_nIndex,m_strLine.Left(m_strLine.Find(' ')));
m_strArray.Add(m_strLine);//m_strArray是CStringArray类型的,它自动将m_strLine存入作为它的数组成员
m_strLine.Empty();//每次都清空m_strLine,为接收下一字段做准备
Invalidate();//使窗口发生重绘,这样回车之后原来输入的内容就消失了
}
else
{
m_strLine+=nChar;//不是回车键则为正常输入
dc.TextOut(0,0,m_strLine);//将其显示出来
}
CView::OnChar(nChar, nRepCnt, nFlags);
}
2、为动态添加的菜单项添加命令响应函数
方法一:
首先创建新的menu菜单资源abc,添加菜单项1、2、3、4,然后通过classwizard为每个菜单项添加消息响应函数,注意ID号为IDM_PHONE1~IDM_PHONE4 。
然后将菜单资源删除,此时只留下了代码;
接着将view源文件中的消息映射移动到注释宏之外:
BEGIN_MESSAGE_MAP(CMenu2View, CView)
//{{AFX_MSG_MAP(CMenu2View)
ON_WM_CHAR()
//}}AFX_MSG_MAP
// Standard printing commands
ON_COMMAND(IDM_PHONE1, OnPhone1)//如此所示
ON_COMMAND(IDM_PHONE2, OnPhone2)//
ON_COMMAND(IDM_PHONE3, OnPhone3)//
ON_COMMAND(IDM_PHONE4, OnPhone4)//
ON_COMMAND(ID_FILE_PRINT, CView::OnFilePrint)
ON_COMMAND(ID_FILE_PRINT_DIRECT, CView::OnFilePrint)
ON_COMMAND(ID_FILE_PRINT_PREVIEW, CView::OnFilePrintPreview)
END_MESSAGE_MAP()
最后编辑消息响应函数
void CMenu2View::OnPhone1()
{
// TODO: Add your command handler code here
CClientDC dc(this);
dc.TextOut(0,0,m_strArray.GetAt(0));
}
方法二:让cmainframe进行消息响应
1、在cmainframe中添加虚函数oncommand
BOOL CMainFrame::OnCommand(WPARAM wParam, LPARAM lParam)
{
// TODO: Add your specialized code here and/or call the base class
int MenuCmdId=LOWORD(wParam);//获得id号
CMenu2View *pView=(CMenu2View*)GetActiveView();//获得当前视类指针
if(MenuCmdId>=IDM_PHONE1 && MenuCmdId<IDM_PHONE1+pView->m_strArray.GetSize())//如果id号没有超出范围
{
CClientDC dc(pView);
dc.TextOut(0,0,pView->m_strArray.GetAt(MenuCmdId-IDM_PHONE1));//显示出来
//MessageBox("Test");
return TRUE;//在此返回,这样就不去调用view的处理函数了
}
return CFrameWnd::OnCommand(wParam, lParam);
}
2、在mainframe源文件中:
#include "Menu2View.h"
3、将view源文件中的#include "Menu2Doc.h"放到view头文件中
后两步是为了避免编译错误。