分享

孙鑫VC视频教程笔记之第六课“菜单”

 Alex@ZW 2010-11-04
1.      消息的分类:

1)       标准消息:
除了WM_COMMAND之外,所有以WM_的消息。
CWnd派生的类,都可以接收这样的消息

2)       命令消息:
来自菜单,加速键或工具栏按钮的消息,这类消息都以WM_COMMAND呈现,在MFC中以菜单栏的ID来区分不同的命令消息,在SDK中,以参数wParam参数来标识。
CCmdTarget派生的类,都可以接收这样的消息

3)       通告消息:
从控件产生的消息,如按钮的单击,列表框的选择均产生这样的消息,为的是向其父窗口(通常是对话框)通知事件的发生,这类消息也是以WM_COMMAND的形式呈现。
CCmdTarget派生的类,都可以接收这样的消息

4)       CWnd类派生于CCmdTarget类,即直接派生于CWnd的类可以接收以上三种消息

 

2.      应用程序菜单栏命令的消息路由顺序是:

先有MainFrame类接收到消息,然后将消息抛给子窗口,即CView类,如果CView类中没有处理函数,则CView类会将该消息抛给CDoc类,如果CDoc类没有处理函数,则CDoc会将消息抛回给CView类,而后由CView将消息抛回给MainFrame,如果MainFrame类中有处理函数,则响应,如果MainFrame类中没有处理函数,则最终将消息抛回给App类处理

 

3.      分清楚子菜单和菜单项的区别:

子菜单是在菜单栏上顶层直接显示的菜单,如文件,编辑,查看等都是子菜单;对于某一个子菜单的下拉列表上所对应的菜单称为菜单项。

 

4.      菜单项操作:

以下所有代码放在CMainFrame类的OnCreate函数中:

CMenu* pFatherMenu=GetMenu(); //得到最菜单栏上的顶层菜单的总的一个类

CMenu* pSubMenu=pFatherMenu->GetSubMenu(0); //得到子菜单,File等,因为没有ID号,只有索引号,当然也可以将子菜单的Popup属性去除设置ID

     

//设置菜单项为缺省,即用粗体显示。注意,一个子菜单中只有一个菜单项能缺省,多则无效,以最终一条设置为准,要不怎么叫缺省菜单呢。

      pSubMenu->SetDefaultItem(0,TRUE);

 

      //在菜单项前设置选择标记

      pSubMenu->CheckMenuItem(1,MF_CHECKED|MF_BYPOSITION);

 

      //在菜单项前设置位图

      CString str;

      //GetSystemMetrics函数中得到系统设置能支持的最大菜单位图值

str.Format("x=%d,y=%d",GetSystemMetrics(SM_CXVSCROLL),GetSystemMetrics(SM_CYVSCROLL));

MessageBox(str); //看一下最大支持的像素是多少,即资源中的位图最大只能设置成前面得到的最大值

     

      //注意bmp对象必须声明为类的变量,否则函数结束,CBitmap对象会析构

      bmp.LoadBitmap(IDB_BITMAP1);

// SetMenuItemBitmaps的第四个参数是选中时显示的位图,第三个参数是将标记取消的时候显示的位图

      pSubMenu->SetMenuItemBitmaps(2,MF_BYPOSITION,&bmp,&bmp);

 

      //将菜单项禁用

//注意要在构造函数中将m_bAutoMenuEnable变量设置为False,否则以下语句执行没有效果。在MSDN中该变量的相关解释是这样的:

When this data member is enabled (which is the default), menu items that do not have ON_UPDATE_COMMAND_UI or ON_COMMAND handlers will be automatically disabled when the user pulls down a menu.

Menu items that have an ON_COMMAND handler but no ON_UPDATE_COMMAND_UI handler will be automatically enabled.

//下面的函数中MF_DISABLED仅仅是禁用,但菜单不会变灰,为使更复合常用习惯,使禁用的菜单同时变灰MF_GRAYED

      pSubMenu->EnableMenuItem(3,MF_BYPOSITION | MF_DISABLED | MF_GRAYED);

 

      //移除整个菜单

SetMenu(NULL);

 

//动态添加菜单栏

CMenu menu; //注意,此处CMenu的对象是局部的,在函数结束的时候会销毁,所以会导致程序出现一些问题,建议将此声明为类的成员变量。

      menu.LoadMenu(IDR_MAINFRAME);

SetMenu(&menu);

menu.Detach(); //如果还是想将CMenu的对象设置成函数的局部变量,可以调用此方法,从而消除程序原先会出现的bug

 

关于Detch函数,在MSDN中关于CMenu类中有这么一段话:

Create a CMenu object on the stack frame as a local(局部变量), then call CMenu’s member functions to manipulate the new menu as needed. Next, call CWnd::SetMenu to set the menu to a window, followed immediately by a call to the CMenu object’s Detach member function. The CWnd::SetMenu member function sets the window’s menu to the new menu, causes the window to be redrawn to reflect the menu change, and also passes ownership of the menu to the window. 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

 

5.      菜单项的命令更新机制:

菜单项状态的维护是依赖于CN_UPDATE_COMMAND_UI消息,谁捕获CN_UPDATE_COMMAND_UI消息,MFC就在其中创建一个CCmdUI对象。我们可以通过手工或利用ClassWizard在消息映射中添加ON_UPDATE_COMMAND_UI宏来捕获CN_UPDATE_COMMAND_UI消息。在后台所做的工作是:操作系统发出WM_INITMENUPOPUP消息,然后由MFC的基类如CFrameWnd接管。它创建一个CCmdUI对象,并与第一个菜单项相关联,调用对象的一个成员函数DoUpdate()。这个函数发出CN_UPDATE_COMMAND_UI消息,这条消息带有指向CCmdUI对象的指针。同一个CCmdUI对象就设置为与第二个菜单项相关联,这样顺序进行,直到完成所有菜单项。

注意:更新命令UI处理程序仅应用于弹出式菜单项上的项目,不能应用于永久显示的顶级菜单项目。

 

6.      右键弹出式菜单:

a.      利用MFC现有创建好的右键菜单:

Project->Add to Project->Components and Controls添加pop menu即可。

向导添加了一个右键菜单资源,并且在CView类中添加了一个函数OnContextMenu,并在该函数中调用了函数TrackPopupMenu,该函数是弹出右键菜单的核心函数。

b.      自己创建右键弹出菜单:

1. 创建菜单资源。

2. CView类中捕获的WM_RBUTTONDOWN消息中添加如下代码:

//代码模仿OnContextMenuMFC自动添加的内容操作

CMenu menu;

menu.LoadMenu(IDR_MENU1);

CMenu* pPopup = menu.GetSubMenu(0); //弹出式菜单只有一个子菜单

ClientToScreen(&point); //如果没有此函数,则菜单弹出时出现的位置是按照屏幕计算的

//弹出右键菜单核心函数

pPopup->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, point.x, point.y,  this); //最后一个参数代表拥有这个菜单的父窗口的指针,如果是this,则CView先进行响应,如果是GetParent(),则CMainFrame进行响应,但如果CView类中有可响应函数,则即使有GetParent,优先得到响应的也是CView中的函数。

 

7.      动态添加子菜单和菜单项:(以上添加菜单子项或菜单项是直接操作菜单资源的,故为静态操作)

CMainFrame类的OnCreate中添加如下代码:

1)       在现有的菜单结尾处添加子菜单或在子菜单的结尾处添加菜单项

CMenu menu;

menu.CreatePopupMenu(); //创建空的弹出菜单,即子菜单

//AppendMenu函数表示在现有的菜单结尾处添加子菜单或在子菜单的结尾处添加菜单项

GetMenu()->AppendMenu(MF_POPUP,(UINT)menu.m_hMenu,"test");   

注意:如果将资源menu声明为局部对象,点击该新建的菜单,会出现运行错误,改正方法是声明为类的成员变量或者调用detech函数。由以往的使用经验可以得出,凡是涉及到的资源类对象,如菜单,位图等如果在函数的内部声明的话,则该资源会在函数退出后析构,则会导致程序的运行错误。在VC的资源视图中的有资源类别:DialogIconMenuCussorBitmapStringTableToolbar,其中Dialog资源也是会有这样的问题,但它有另一种解决办法,即将局部对象声明为指针。StringTable好像不存在上述的问题,不知道这个想法是否正确。

2)       在现有的菜单中间添加菜单项或子菜单:

GetMenu()->InsertMenu(3,MF_POPUP|MF_BYPOSITION,(UINT)menu.m_hMenu,"test");

3)       给新创建的子菜单中创建菜单项:

CMenu menu;

menu.CreatePopupMenu(); //创建空的弹出菜单,即子菜单

GetMenu()->AppendMenu(MF_POPUP,(UINT)menu.m_hMenu,"test");

menu.AppendMenu(MF_STRING,110,"item1");

menu.AppendMenu(MF_STRING,111,"item2");

menu.Detach();

4)       删除指定菜单项:DeleteMenu;

 

8.      给动态创建的菜单创建消息响应函数:

  1. Resource.h中定义一个菜单项ID,如#define IDM_HELLO 110(见上面代码,在定义完该资源ID后就可以将上述的代码中的110换成IDM_HELLO)
  2. CMainFrame中定义消息响应函数原型:afx_msg void OnHello();
  3. 消息映射:对于菜单命令,是通过命令消息,即WM_COMMAND,在消息循环中添加ON_COMMAND(IDM_HELLO, OnHello)
  4. 编写消息响应函数源码。

 

9.      CObArrayCStringArray:两个非常有用的数组类

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多