1.菜单栏、工具栏、状态栏
2.菜单命令响应函数
菜单编辑器—添加菜单项—输入ID号(ID后加一个标识资源类型的字母,比如:IDM_菜单资源;IDC_光标资源;IDI_图标资源)—为该菜单添加命令响应函数(ClassWizard)
3.菜单命令的路由
(1)CTestApp和CTestDoc都不是从CWnd类派生的,他们都没有MessageBox成员函数,可以使用全局函数中MessageBox函数,或使用应用程序框架函数AfxMessageBox()
函数原型:int AfxMessageBox(LPCTSTR lpszText,UINT nType=MB_OK,UINT uIDHelp=0);
(2)响应菜单项命令的顺序是:视类、文档类、框架类、最后才是应用程序类
(3)消息的分类:标准消息:除了WM_COMMAND之外,所有以WM_开头的消息都是标准消息,从CWnd派生的类,都可以接受到这类消息
命令消息:来自菜单、加速键或工具栏按钮的消息,这类消息都以WM_COMMAND形式呈现。在MFC中,通过菜单项的标识(ID)来区分不同的命令消息;在SDK中,通过消息的wParam参数识别,从CCmdTarget派生的类,都可以接受这类消息。
通告消息: 由控件产生的消息,例如按钮的单击、列表框的选择等都会产生这类消息,目的是向其父窗口(通常是对话框)通知事件的发生,这类消息也是以WM_COMMAND形式呈现的,从CCmdTarget派生的类,都可以接受这类消息。
(4)AfxWndProc—AfxCallWndProc—WindowProc(CWnd成员函数)—OnWndMsg(调用相应函数处理标准消息)—(OnCommand(完成命令消息路由))或(OnNotify(完成通告消息路由))—OnCmdMsg
(5)菜单命令路由过程:框架类收到菜单命令消息,交给他的子窗口(视类),由视类首先进行处理;视类如果不能处理,就交给文档类;文档类如果不能处理,就把消息交还给视类,视类再交还给框架类;框架类如果不能处理,就交给应用程序类。
4.基本菜单操作
(1)菜单—菜单项
(2)标记菜单(前面有对号)
1.获得菜单栏,在框架窗口中获得指向菜单栏的指针:CWnd成员函数GetMenu
函数原型:CMenu* GetMenu() const;
2.获取一个菜单的子菜单:CMenu类的成员函数:GetSubMenu()//CMenu类是Windows菜单句柄HMENU的封装,提供一些与菜单操作有关的成员函数。如:创建、销毁等
函数原型:CMenu* GetSubMenu(int nPos) const;//参数nPos指定了子菜单的索引号。(返回的值与上面函数不同,上面返回的是菜单栏的指针,该函数返回的是菜单栏中的某一子菜单的指针。主要是两个函数所属的类不同)
3.设置一个标记菜单:CMenu类的CheckMenuItem()函数
函数原型:UINT CheckMenuItem(UINT nIDcheckItem,UINT nCheck);
//该函数的作用是为菜单项添加一个标记,或者移出菜单项的标记
// nIDcheckItem指定需要处理的菜单项(如果是索引号的话,是从零开始的)
// nCheck指定怎样设置菜单以及如何定位该菜单项的位置。他的取值可以是MF_CHECKED
//(设置标记)和MF_UNCHECKED(移除)与MF_BYPOSITION(菜单项的位置,即第一个参数是菜单项的索引号)或MF_BYCOMMAND(菜单项的命令,第一个参数是菜单项的ID)的组合。
Eg:GetMenu()->GetSubMenu(0)->CheckMenuItem(0,MF_BYPOSITION | MF_CHECKED);
(3)默认菜单项(粗体显示)
1.创建默认菜单项:CMenu类的SetDefaultItem()函数
函数原型:BOOL SetDefaultItem(UINT uItem,BOOL fBypos = FALSE);
//第一个参数取值由第二个参数决定,可以是新的默认菜单项的标识或位置索引(-1,无)
//第二个参数 FALSE:菜单项标识;TRUE:菜单项位置索引
Eg:GetMenu()->GetSubMenu(0)->SetDefaultItem(1,true);
2.一个子菜单只能有一个默认菜单项
(4)图形标记菜单(菜单项前面带有图形)
1.创建图形标记菜单项:CMenu类的SetMenuItemBitmaps函数
函数原型:BOOL SetMenuItemBitmaps(
UINT nPosition,//取值为标识或索引(由第二项决定)
UINT nFlags,//MF_BYCOMMAND或MF_BYPOSITION
const CBitmap* pBmpUnchecked,//菜单项未选中时显示
const CBitmap* pBmpChecked//选中菜单项时显示的位图
);
2.得到图形标记菜单上显示的位图尺寸(若位图太大,只显示左上的一部分)
函数原型:int GetSystemMetrics(int nIndex);//参数用来指定希望获取哪部分系统信息,当该参数的值为SM_CXMENUCHECK或 SM_CYMENUCHECK时,将获取标记菜单项上标记图形的默认尺寸(宽、高)
3.CString类提供一个Format函数,用于把内容按一定格式存放在字符串对象
Eg:bitmap.LoadBitmap(IDB_BITMAP2);
GetMenu()->GetSubMenu(0)->SetMenuItemBitmaps(2,MF_BYPOSITION,&bitmap,&bitmap);
(5)禁用菜单项:CMenu类的成员函数:EnableMenuItem()
函数原型:UINT EnableMenuItem(UINT nIDEnableItem,UINT nEnable);
//该函数的作用是:设置菜单项的状态,能够使用,禁用或者变灰显示
//第一个参数的含义由第二个参数决定
//MF_DISABLED(禁用),MF_ENABLED(可用),MF_GRAYED(变灰),MF_COMMAND,MF_BYPOSITION的组合 todo:常常把禁用和变灰两个标识放在一起使用
1.MFC为菜单提供了一种命令更新机制,程序运行时,根据此机制去判断哪些菜单可以使用,哪些不能用。默认情况下,所有菜单项的更新都是由MFC的命令更新机制完成的,如果我们想自己更改菜单项的状态,就必须在CMainFrame的构造函数中把m_bAutoMenuEnable变量的值设为false;之后,我们对菜单项的状态更新才能起作用。
m_bAutoMenuEnable = FALSE;(在构造函数中)
GetMenu()->GetSubMenu(0)->EnableMenuItem(3,MF_BYPOSITION|MF_DISABLED|MF_GRAYED);(在OnCreate函数末尾)
2.将m_bAutoMenuEnable设置为FALSE之后,MFC不再利用它的菜单命令更新机制去判断菜单是否能用,这些判断,就需要我们自己完成了!
(6)移除和装载菜单:CWnd类的成员函数SetMenu()
函数原型:BOOL SetMenu(CMenu* pMenu);//这里pMenu指向一个新菜单对象,如果这个参数值为NULL,则当前菜单就被移除了
CMenu menu;//memu是局部变量,会出错;应该(1)定义为全局变量或者(2)//用Detach把菜单句柄和这个菜单对象分离,这样局部对象生命周期结束时,该菜单不会//被销毁。
menu.LoadMenu(IDR_MENU1);//加载资源
SetMenu(&menu);
memu.Detach();
5.MFC菜单命令更新机制
(1)MFC编程时,菜单项状态的维护依赖于CN_UPDATE_COMMAND_UI消息,我们可以通过手工,或利用ClassWizard在消息映射中添加ON_UPDATE_COMMAND_UI宏来捕获CN_UPDATE_COMMAND_UI消息
(2)消息处理函数有一个CCmdUI指针类型的参数,利用它可以决定一个菜单项是否可用,是否有标记,还可以改变菜单项的文本
(3)MFC命令更新机制:当要显示菜单时,操作系统发出WM_INITMENUPOPUP消息,CFrameWnd接管,创建一个CCmdUI对象,并与程序的第一个菜单项相关联,调用该对象的一个成员函数DoUpdate()这个函数发出CN_UPDATE_COMMAND_UI消息,这条消息带有一个指向CCmdUI对象的指针,这时,系统会判断是否存在一个ON_UPDATE_COMMAND_UI宏区捕捉这个菜单项消息。若有,就调用相应的消息响应函数处理。在这个函数中,可以利用传递过来的CCmdUI对象区调用相应的函数,使菜单项可以使用或禁用等。更新完第一个菜单项后,继续更新下一个,直到处理完所有的。
(4)CCmdUI类的Enable函数原型为:virtual void Enable(BOOL bOn = TRUE);
(5)如果要把工具栏上的一个按钮和菜单栏中的某个菜单项相关联,只要将他们的ID设置为同一标识就可以了
(6)如果要在程序中设置某个菜单项的状态,首先通过ClassWizard为这个菜单项添加 UPDATE_COMMAND_UI消息响应函数,然后在这个函数中进行状态的设置即可。
6.快捷菜单(右键菜单,上下文菜单)
(1)添加组件实现此功能:
Project—Add To Project—Components and Control—Visual C++ Components目录 — Pop-up Menu—添加到:视类—完成
插入组件后程序中添加了一下两处内容
1.CG_IDR_POPUP_TEST_VIEW菜单资源
2. 为CTestView类添加了一个函数:OnContextMenu;在程序运行时,当用鼠
标右键单击窗口时,程序就会调用这个函数。
这个函数内部调用了TrackPopupMenu函数来显示一个快捷菜单
函数原型:
BOOL TrackPopMenu(UINT nFlags,int x,int y,CWnd* pWnd,LPCRECT lpRect = NUll);
// nFlags指定菜单在屏幕上显示的位置
//x,y指定显示位置处的横纵坐标
// pWnd指定快捷菜单的拥有者
//指定一块矩形区域,如果用户在这个区域内单击鼠标,菜单仍保持显示,否则快捷菜单消//失,若为NULL,快捷菜单范围外单击鼠标,这个菜单就消失。
(2)添加代码实现此功能
1.为Menu程序增加一个新的菜单资源。
2.给CTestView添加WM_RBUTTONDOWN消息响应函数,消息响应函数内容参照
OnContextMenu函数
CMenu menu;
menu.LoadMenu(IDR_MENU1);
CMenu* pPopup = menu.GetSubMenu(0);
ClientToScreen(&point);// 客户区坐标转换为屏幕坐标((3)添加代码)
pPopup->TrackPopupMenu(TPM_LEFTALIGN|TPM_RIGHTBUTTON,point.x,point.y,this);
3.菜单可以显示,但位置不对,因为TrackPopupMenu函数中的X和Y参数都是屏幕坐标,而鼠标右键响应函数中传入的坐标是窗口客户区的坐标,这样就需要我们把客户区坐标转换为屏幕坐标:ClientToScreen()函数;
4.为程序添加快捷菜单上各菜单项命令的响应函数。(可以不创建菜单资源类)
7.动态菜单操作:在程序运行过程根据需要对程序的菜单进行添加、插入、删除操作.这些操作包括两种情况:针对弹出菜单的动态操作,针对菜单项的动态操作
(1)用代码添加菜单项目(子菜单和菜单项):CMenu类的成员函数AppendMenu()
函数原型:BOOL AppendMenu (
UINT nFlags,//指定新添加的菜单项目的状态信息
UINT_PTR nIDNewItem = 0,//取决于第一个参数(句柄(MF_POPUP)或ID)
LPCTSTR lpszNewItem = NULL//取决于第一个参数
);//函数作用是把一个新菜单项目添加到一个指定菜单项目的末尾
//状态:MF_CHECKED(标记)、MF_ENABLED(可用)、MF_DISABLED(禁用)、MF_GRAYED(变灰)、MF_SEPAEATOR(分隔栏)、MF_POPUP(弹出菜单项)、MF_MENUBREAK(更改菜单项目的显示方式)
(2)为了添加菜单,首先要创建一个菜单对象:CMenu成员函数CreatePopupMenu()
这个函数的作用是,创建一个弹出菜单,然后将其与一个CMenu对象关联起来
(3)插入菜单项目(菜单项目之间插入一个新的菜单项目):CMenu类InsertMenu
函数原型:BOOL InsertMenu(
UINT nPosition,//新菜单项目插入位置(该位置前插入)
UINT nFlags, //MF_BYPOSITION、MF_BYCOMMAND、上面所有
UINT_PTR nIDNewItem = 0,//同AppendMenu
LPCTSTR lpszNewItem = NULL//同AppendMenu
);
(4)删除菜单:CMenu成员函数DeleteMenu()
函数原型:BOOL DeleteMenu(UINT nPosition,UINT nFlags);//参数含义同上
(5)动态添加的菜单项的命令响应
1.在Resource.h文件中,添加新的ID #define IDM_HELLO ;
2.在AppendMenu等函数中可以使用定义的ID,然后就可以为其添加命令消息响应函数了
3.在响应这个菜单项命令的程序类的头文件中添加消息响应函数原型AFX_MSG之间
4.在响应这个菜单项命令的程序类的源文件中的消息映射表中添加消息映射
5.实现菜单命令消息响应函数的函数体。
8.CWnd有一个名为Invalidate的成员函数,该函数的作用是让窗口的客户区无效,这样,当下一条WM_PAINT消息发生时,窗口就会被更新
函数原型:void Invalidate(BOOL bErase = TRUE);//TRUE时,窗口背景被檫除,否则保留窗口背景。
9.框架类截获菜单命令消息:在框架类中重写OnCommand消息。
函数原型:virtual BOOL OnCOmmand(WPARAM wParam,LPAPAM lParam);