http://mail.ustc.edu.cn/~bywang/programm/vc/sx/5.html
IDM_:菜单
IDI_:图标
IDC_:光标
命令消息传递的顺序: View clicked-->Doc clicked-->MainFrame clicked-->App clicked
视类-->文档类-->框架类-->应用程序类
一、 消息的分类:
消息的分类:标准消息,命令消息,通告消息。
[标准消息]:除WM_COMMAND之外,所有以WM_开头的消息。从CWnd派生的类,都可以接收到这个消息
[命令消息]:来自菜单、加速键或工具栏按钮的消息。这类消息都以WM_COMMAND呈现。
在MFC中,通过菜单项的标识(ID)来区分不同的命令消息;在SDK中,通过消息的wParam参数识别。从CCmdTarget派生的类,都可以接收到这类消息
[通告消息]:由控件产生的消息,例如,按钮的单击,列表框的选择等均产生此类消息,为的是向其父窗口(通常是对话框)通知事件的发生。这类消息也是以WM_COMMAND形式呈现。从CCmdTarget派生的类,都可以接收到这类消息
注意:
1)从CWnd派生的类,都可以接收到[标准消息]、[命令消息]和[通告消息]。
2)从CCmdTarget派生的类,都可以接收到[命令消息]和[通告消息]。
3)CCmdTarget是CWnd的父类
CMenuDoc和CMenuApp都不能接收标准消息(?)
二、 菜单消息传递过程
MFC中菜单项消息如果利用ClassWizard来对菜单项消息分别在上述四个类中进行响应,则菜单消息传递顺序:View类èDoc类èCMainFrame类èApp类。菜单消息一旦在其中一个类中响应则不再在其它类中查找响应函数。
当点击一个菜单项的时候,最先接受到菜单项消息的是CMainFrame框架类,CMainFrame框架类将会把菜单项消息交给它的子窗口View类,由View类首先进行处理;如果View类检测到没对该菜单项消息做响应,则View类把菜单项消息交由文档类Doc类进行处理;如果Doc类检测到Doc类中也没对该菜单项消息做响应,则Doc类又把该菜单项消息交还给View类,由View类再交还给CMainFrame类处理。(总而言之,Doc类就是个矬子)如果CMainFrame类查看到CMainFrame类中也没对该消息做响应,则最终交给App类进行处理。
命令消息的路由:
AfxWndProc-->AfxCallWndProc-->WindowProc-->OnWndMsg-->
-->1 OnCommand
-->2 OnNotify
-->OnCmdMsg
三、 菜单指针的获取,及相关设置
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) if (!m_wndStatusBar.Create(this) || // TODO: Delete these three lines if you don't want the toolbar to //GetMenu()->GetSubMenu(0)->CheckMenuItem(0,MF_BYPOSITION | MF_CHECKED); // GetMenu()->GetSubMenu(0)->EnableMenuItem(1,MF_BYPOSITION | MF_DISABLED | MF_GRAYED); SetMenu(NULL); CMenu menu; |
在CMainFrame::OnCreate下可以直接实验以下操作
几个相关和重要的函数
CMenu* GetMenu( ) ;//CWnd::GetMenu得到(整个)窗口菜单栏对象指针。
CMenu* GetSubMenu(int nPos ) ; //CMenu::GetSubMenu获得指向弹出菜单对象指针
UINT CheckMenuItem( );//CMenu::CheckMenuItem Adds check marks to or removes check marks from menu items in the pop-up menu.
The CheckMenuItem function sets the state of the specified menu item's check-mark attribute to either selected or clear.
Syntax
DWORD CheckMenuItem(
HMENU hmenu, UINT uIDCheckItem,//由uCheck确定是ID号还是索引值 UINT uCheck );uCheck值:可以或成: MF_BYCOMMAND | CHECKED
MF_BYCOMMAND //ID号
- Indicates that the uIDCheckItem parameter gives the identifier of the menu item. The MF_BYCOMMAND flag is the default, if neither the MF_BYCOMMAND nor MF_BYPOSITION flag is specified.
MF_BYPOSITION //索引值- Indicates that the uIDCheckItem parameter gives the zero-based relative position of the menu item.
BOOL SetDefaultItem();//CMenu::SetDefaultItem Sets the default menu item for the specified menu.
BOOL SetMenuItemBitmaps( );//CMenu::SetMenuItemBitmaps 设置位图标题菜单。
UINT EnableMenuItem();//CMenu::EnableMenuItem使菜单项有效,无效,或变灰。
BOOL SetMenu( CMenu* pMenu );//CWnd::SetMenu在当前窗口上设置新菜单或移除菜单。
HMENU Detach( );//CMenu::Detach Detaches a Windows menu from a CMenu object and returns the handle.
获取菜单的宽和高:
GetSystemMetrics(SM_CXMENUCHECK),
GetSystemMetrics(SM_CYMENUCHECK)
例子:
1, 给菜单项打上标记
GetMenu()->GetSubMenu(0)->CheckMenuItem(0,MF_BYPOSITION | MF_CHECKED);
GetMenu()->GetSubMenu(0)->CheckMenuItem(ID_FILE_NEW,MF_BYCOMMAND | MF_CHECKED);
//索引从0开始
2, 设置缺省菜单项
GetMenu()->GetSubMenu(0)->SetDefaultItem(1,TRUE);
GetMenu()->GetSubMenu(0)->SetDefaultItem(ID_FILE_OPEN);//可以看到文件-->打开一项为粗体显示了已经
再加一个: GetMenu()->GetSubMenu(0)->SetDefaultItem(4,TRUE);
结果:打印没有变成粗体显示!!
猜测:会不会是只允许有一个缺省菜单项呢? 是!
但注释掉前面打开项,再运行.还是不行,打印还是没有变成粗体显示.
问题:索引写错了,注意分隔符!!
3, 图形标记菜单
先创建图形,注意底色不要是白色
m_bitmap.LoadBitmap(IDB_BITMAP1);//注意要声明成MainFrame类的成员变量
GetMenu()->GetSubMenu(0)->SetMenuItemBitmaps(0,MF_BYPOSITION,&m_bitmap,&m_bitmap);
The SetMenuItemBitmaps function associates the specified bitmap with a menu item. Whether the menu item is selected or clear, the system displays the appropriate bitmap next to the menu item.
Syntax
BOOL SetMenuItemBitmaps(
HMENU hMenu, UINT uPosition, UINT uFlags, HBITMAP hBitmapUnchecked,//未选中时显示一个图形 HBITMAP hBitmapChecked//选中时显示另一个图形 );中间出现问题:位图太大,只显示左上解的部分-->应该将位图缩小
应该缩多小?
函数GetSystemMetrics
The GetSystemMetrics function retrieves various system metrics (widths and heights of display elements) and system configuration settings. All dimensions retrieved by GetSystemMetrics are in pixels.
int GetSystemMetrics( int nIndex );此处参数我们选取:
SM_CXMENUCHECK 71
SM_CYMENUCHECK
Width of the default menu check-mark bitmap, in pixels.答案:13*13
4, 屏蔽菜单,使之不能用
(需要在CMainFrame::CMainFrame()中设置m_bAutoMenuEnable=FALSE;)
GetMenu()->GetSubMenu(0)->EnableMenuItem(1,MF_BYPOSITION | MF_DISABLED | MF_GRAYED);//MF_DISABLED | MF_GRAYED一般一起使用
// The code fragment below shows how to disable (and gray out) the // File/New menu item. // NOTE: m_bAutoMenuEnable is set to FALSE in the constructor of // CMainFrame so no ON_UPDATE_COMMAND_UI or ON_COMMAND handlers are // needed, and CMenu::EnableMenuItem() will work as expected. CMenu* mmenu = GetMenu(); CMenu* submenu = mmenu->GetSubMenu(0); submenu->EnableMenuItem(ID_FILE_NEW, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
5, 取消和加载菜单
用此功能,可以动态的修改菜单
SetMenu(NULL);//取消菜单项
CMenu menu;
menu.LoadMenu(IDR_MAINFRAME);
SetMenu(&menu);
//问题:cmenu是局部对象,打开时出现问题;解决方法如下-->
menu.Detach();//菜单句柄和对象断开,使对象析构时不销毁菜单
The call to Detach detaches the HMENU from the CMenu object, so that when the local CMenu variable passes out of scope, the CMenu object destructor does not attempt to destroy a menu it no longer owns. The menu itself is automatically destroyed when the window is destroyed.//直到窗口销毁才销毁菜单
四、 命令更新机制
菜单项状态的维护是依赖于CN_UPDATE_COMMAND_UI消息,谁捕获CN_UPDATE_COMMAND_UI消息,MFC就在其中创建一个CCmdUI对象。
CCmdUI
OnEidtCut响应函数
在后台操作系统发出WM_INITMENUPOPUP消息,然后由MFC的基类如CFrameWnd接管并创建一个CCmdUI对象和第一个菜单项相关联,调用对象成员函数DoUpdate()(注:这个函数在MSDN中没有找到说明)发出CN_UPDATE_COMMAND_UI消息,这条消息带有指向CCmdUI对象的指针。此后同一个CCmdUI对象又设置为与第二个菜单项相关联,这样顺序进行,直到完成所有菜单项。
更新命令UI处理程序仅应用于弹出式菜单项上的项目,不能应用于永久显示的顶级菜单项目。
工具栏上图标如何与菜单关联? 直接将ID号设为一样即可...
注意:以下两语句的效果不同。对菜单项一样,对工具栏索引对应不一样
//对于剪贴来说,菜单项和工具栏上的索引不一样!!!!!!!
//所以经验是: 常用ID号,不要用索引号!!老是出错...
if(0==pCmdUI->m_nIndex)pCmdUI->Enable(FALSE);
if(ID_FILE_NEW==pCmdUI->m_nID)pCmdUI->Enable(FALSE);