VC++深入详解(4):MFC菜单设计

来源:互联网 发布:网上挣钱软件 编辑:程序博客网 时间:2024/06/11 07:03
首先,菜单是一种资源,在资源视图中,我们可以找到默认的单文档应用程序的菜单。然后还有一些概念性的东西需要交代清楚:整个菜单上的那些“文件”、“编辑”、“查看”等等内容,称为“顶层菜单”,双击它们,你会发现它们没有ID,而且它们是“弹出”的,它们不能响应命令;而点击子菜单的“打开”、“新建”等内容,可以响应命令这些内容称为菜单项。

我们发现,不论是新建的菜单,还是子菜单,在最后总有一个虚线框起来的空位置,双击它,就能以所见即所得的方式增加它们。你也可以在已有的菜单上双击,修改菜单。对于默认添加的顶层菜单,它是它是弹出式的,不能响应命令,我们可以可以将PopUp选项去掉,然后给他设置一个编号:IDM_TEST。有了一个可以响应命令的菜单,我们可以通过类向导为它添加响应命令:在菜单上点击鼠标右键,选择建立类向导,然后选择你要响应的ID:IDM_TEST,选择你要响应的类(这里选择框架类),然后在Message中选择COMMAND,点击添加函数,编辑函数就可以了。我们在这里做一个简单的响应:使用MessageBox来发送消息:

MessageBox("MainFrame Clicked!");
然后重新编译后,我们点击TEST菜单,就会收到这个消息框发送的消息了。
我们除了可以让框架类响应菜单之外,我们也可以让其他的类响应这个菜单的消息。添加的做法基本相同,但是在文档类和应用程序类中,不能直接使用MessageBox(因为他们不是从CWnd类中派生的,没有这个成员函数),而应该使用AfxMessageBox。
设定完成后,当我们点击TEST菜单后,响应的是View类,如果把View类的响应函数删掉,那么响应的是文档类,如果把文档类的响应函数删掉,那么响应的是框架类,如果把框架类的删掉,那么响应的是应用程序类。
那么这种消息响应机制到底是什么样的呢?
得从消息的分类说起,windows系统中,消息分为3类:
1.标准消息:WM开头的,除了WM_COMMAND之外的消息都是标准消息,像我们之前使用过的WM_CHAR、WM_LBUTTONDOWN等等。从CWnd派生来的类都能接受这种消息。
2.命令消息:WM_COMMAND,它们是由菜单、工具栏等发出的消息。不同消息通过wParam来区别,从CCmdTarget类派生出来的的类都能相应这些消息。
3.通告消息:由控件产生。它们也是WM_COMMAND消息,从CCmdTarget类派生出来的的类都能相应这些消息。
在我们的例子中,文档类和应用程序类都派生于CCmdTarget,所以它们可以接受命令消息,但它们不是从CWnd派生的,所以不能接受标准消息。
当你为一个类添加了响应消息后,ClassWizzard会在3处修改代码:
1.这个类的头文件,中通过注释宏声明了命令响应函数原型:
//{{AFX_MSG(CCH_6_MENU1View)afx_msg void OnTest();//}}AFX_MSG
2.源文件中,增加了消息映射:

BEGIN_MESSAGE_MAP(CCH_6_MENU1View, CView)//{{AFX_MSG_MAP(CCH_6_MENU1View)ON_COMMAND(IDM_TEST, OnTest)//}}AFX_MSG_MAP
3.源文件中,增加了消息响应函数:

void CCH_6_MENU1View::OnTest() {// TODO: Add your command handler code hereMessageBox("View Test!");}
这与前面介绍的标准消息的响应是类似的。但是命令消息的路由却有点复杂:
MFC把窗口过程函数替换为AfxWndProc函数,这个函数调用AfxCallWndProc函数,而它将调用WndProc函数。这个函数是CWnd的成员函数。它将调用OnWndMsg函数。在这里,需要对消息进行判断,如果是标准消息,则通过消息映射来处理,如果是命令消息则交由OnCommand函数来处理,如果是通告消息,则交由OnNotify消息,而二者都会调用OnCmdMsg函数处理。
然后分析我们的例子:点击菜单项时,接收到命令的消息的是框架类,框架类交由视类处理,视类如果没有响应,则交由文档类,如果文档类没有响应,交还给框架类,如果框架类没有响应,则交给应用程序类。


下面我们将要详细的介绍对菜单的基本操作。
给菜单加上标记:
首先,这些代码应该放在哪里?应该放在对WM_CREAT消息的响应下。如何获取菜单的呢?通过CWnd的成员函数GetMenu来获得。这个函数返回一个指向CMenu类的指针。CMenu是MFC封装的关于菜单的类,其中有一个成员函数GetSubMenu来获得子菜单,找到子菜单以后,通过CheckMenuItem来给菜单加上标记,这个函数就可以用两种办法访问菜单项,具体如下:

//GetMenu()->GetSubMenu(0)->CheckMenuItem(0,MF_CHECKED | MF_BYPOSITION );GetMenu()->GetSubMenu(0)->CheckMenuItem(ID_FILE_NEW,MF_CHECKED | MF_BYCOMMAND);
在CheckMenuItem第一种方法使用的是序号:在第二个参数中表明MF_BYPOSITION ,第一个参数填上序号即可,注意是从0开始的;当第二个参数选择MF_BYCOMMAND时,第一个参数就要使用ID号了。
添加默认菜单项:
这也是通过CMenu的成员函数SetDefaultItem来完成的。而这个函数也是既可以使用ID,也可以使用序号使用,默认情况下,第二个参数为FALSE,使用ID访问:

//GetMenu()->GetSubMenu(0)->SetDefaultItem(ID_FILE_NEW);GetMenu()->GetSubMenu(0)->SetDefaultItem(0,TRUE);
对于使用序号来访问,有一点需要注意,就是子菜单中的分隔符也占了一个序号,比如“另存为”和“打印”之间就是。所以如果你想通过序号把打印设为默认菜单项,应该使用:

GetMenu()->GetSubMenu(0)->SetDefaultItem(5,TRUE);
最后,要注意一个子菜单只能设置一个默认菜单项。
给菜单加上图形标记:
这可以通过SetMenuItemBitmaps 函数来完成。首先需要一个Bitmap类型的成员变量m_bitmap来承载位图,然后调用这个函数。

m_bitmap.LoadBitmap(IDB_BITMAP1);GetMenu()->GetSubMenu(0)->SetMenuItemBitmaps ( ID_FILE_NEW , MF_BYCOMMAND , &m_bitmap , &m_bitmap );
我们发现虽然可以添加,但是位图的大小却不太合适,我们可以通过GetSystemMetrics(SM_CXMENUCHECK)来获取菜单的图形标记的长度信息,发现默认的大小为13,我们重新修改一下自己的位图就行了。
禁用菜单项:
想要屏蔽某些菜单的功能,可以通过EnableMenuItem 来实现。

GetMenu()->GetSubMenu(0)->EnableMenuItem (1,MF_BYPOSITION | MF_DISABLED);
但我们发现这个设置好像不起作用。我们发现,在这个函数提供的例子中说道:要把m_bAutoMenuEnable 在构造函数设为false,就不需要响应ON_UPDATE_COMMAND_UI 和ON_COMMAND 的消息处理函数了。稍后我们在讨论这个机制,这里,只需要把在构造函数中把m_bAutoMenuEnable设为false就行了。修改以后,我们还发现原来编辑菜单中的东西也能使用了。


移除和装载菜单:
这里要使用的函数是SetMenu。他接受一个指向CMenu的指针,如果把这个指针设为NULL,就可以移除菜单:

SetMenu(NULL);
如果想装载菜单,可以:

CMenu menu;menu.LoadMenu(IDR_MAINFRAME);SetMenu(&menu);
看起来就没有问题了,但是我们很容易就会发现,因为这里的menu是一个局部变量,所以程序执行完以后就析构了。所以程序在运行时会出问题。按照我们以前的习惯,可以将menu定义为成员变量。其实还有一种解决办法:使用CMenu的成员函数Detach 。他将菜单句柄与菜单对象分离,这样当menu的生命周期结束以后,不会销毁那个本来是有他管理,但现在已经从他身上分离出去的那个菜单,而这个菜单会在窗口被关闭时自动销毁。


如何禁用或者使用菜单
菜单状态维护依赖于CN_UPDATE——COMMAND_UI消息。我们可以通过响应这个消息来确定这个菜单想是否能被使用,具体的说,可以调用Enable函数来完成:

void CMainFrame::OnUpdateEditCut(CCmdUI* pCmdUI) {// TODO: Add your command update UI handler code here//pCmdUI->Enable(FALSE);pCmdUI->Enable();}
我们发现,不仅菜单上的剪切可以使用了,工具栏上的也可以使用了,这是为什么呢?我们子资源视图中找到工具栏,双击剪切的图标,发现它的ID与菜单上剪切的图标ID是一样的!


快捷菜单
所谓的快捷菜单,就是平时使用时,点击鼠标右键出现的菜单。其实VC++已经帮我们实现了这个功能:选择project->add to project->components and controls,然后选择visual c++ components目录,然后选择Pop-Up menu就行了。但是要注意,增加到view类中。










原创粉丝点击