第6课菜单

消息分类

1.      当对某菜单添加消息响应函数时,4个类的消息响应优先次序分别是:1.View;2.CDOC;3.CMainFrame.4.CWinAPP.为什么?请参阅《深入浅出》

 

2.MFC中的顶层菜单默认为弹出菜单(Pop-up),它是不能用来作命令响应的,当取消Pop-up选项后可接受命令响应。

 

3.      MFC中菜单项消息如果利用ClassWizard来对菜单项消息分别在上述四个类中进行响应,则菜单消息传递顺序:View类--Doc类--CMainFrame类--App类。菜单消息一旦在其中一个类中响应则不再在其它类中查找响应函数。

菜单消息与前面介绍的标准消息的实现机制是相类似的,都是在消息响应函数原型(头文件),消息映射宏(源文件)和消息函数实现(源文件)中添加代码。注意:文档类与应用程序类都是由CCmdTarget类派生,所以可以接收菜单命令消息,但不能接收标准消息(只能由CWnd类派生才可以)。

具体消息路由过程:

当点击一个菜单项的时候,最先接受到菜单项消息的是CMainFrame框架类,CMainFrame框架类将会把菜单项消息交给它的子窗口View类,由View类首先进行处理;如果View类检测到没对该菜单项消息做响应,则View类把菜单项消息交由文档类Doc类进行处理;如果Doc类检测到Doc类中也没对该菜单项消息做响应,则Doc类又把该菜单项消息交还给View类,由View类再交还给CMainFrame类处理。如果CMainFrame类查看到CMainFrame类中也没对该消息做响应,则最终交给App类进行处理。

对于父窗口的菜单,子窗口拥有优先响应权

但是对于子窗口的菜单,父窗口是无法响应的

 

4.消息分类:a;标准消息(以WM_开头的消息,但不包括ON_COMMAND);b;命令消息 ON_COMMAND(IDM_PHONE1, OnPhone1),菜单和工具栏的消息。c.通告消息:按钮,列表框发出的消息。

CCmdTarget只能接受命令消息。而从CCmdTarget派生的CWnd可以接收命令消息,也可以接受标准消息。

总结:凡是从CWnd派生的类,既可以接收标准消息,也要以接收命令消息和通告消息。而对于那些从CCmdTarget派生的类,则只能接收命令消息和通告消息,不能接收标准消息

 

5,要disable File/New菜单项必须CMainFrame中将m_bAutoMenuEnable设置为false先

// The code fragment below shows how todisable (and gray out) the

// File\New menu item.

// NOTE: m_bAutoMenuEnable is set to FALSEin the constructor of

// CMainFrame so no ON_UPDATE_COMMAND_UI orON_COMMAND handlers are

// needed, and CMenu::EnableMenuItem() willwork as expected.

在MFC中MFC为我们提供了一套命令更新机制,所有菜单项的更新都是由这套机制来完成的。所以要想利用CMenu::EnableMenuItem来自己控制菜单使用或不使用变灰等,必须要在CMainFrame的构造函数中将变量m_bAutoMenuEnable设置为FALSE。

原因是m_bAutoMenuEnable为true会导致显示的时候更新从而将菜单项设置为enable

 

6,HMENU Detach( );//CMenu::Detach;断开一个菜单资源与相关的类对象句柄关联,可以定义局部对象,在使用完后调用Detach函数,则不会因为局部对象影响使用,不会因为CMenu的析构导致菜单销毁,菜单会在窗口销毁的时候自动销毁

 

7.确定菜单的索引号,注意从0开始,分隔符也算数。什么叫弹出菜单(Popup Menu)?一个子菜单只能有一个缺省菜单。 //GetMenu()->GetSubMenu(0)->SetDefaultItem(5,TRUE);

 str.Format("x=%d,y=%d",GetSystemMetrics(SM_CXMENUCHECK),

  GetSystemMetrics(SM_CYMENUCHECK));//获得系统的菜单的位图的大小。

/* SetMenu(NULL);//移除菜单 CMenu menu;

 menu.LoadMenu(IDR_MAINFRAME);

 SetMenu(&menu);

 menu.Detach();*/增加菜单,此处detach(),如果是局部变量。

8,命令更新机制:

菜单项状态的维护是依赖于CN_UPDATE_COMMAND_UI消息,谁捕获CN_UPDATE_COMMAND_UI消息,MFC就在其中创建一个CCmdUI对象。

菜单要显示的时候,在后台操作系统会发出WM_INITMENUPOPUP消息,然后由MFC的基类如CFrameWnd接管并创建一个CCmdUI对象和第一个菜单项相关联,调用对象成员函数DoUpdate()(注:这个函数在MSDN中没有找到说明)发出CN_UPDATE_COMMAND_UI消息,这条消息带有指向CCmdUI对象的指针。此后同一个CCmdUI对象又设置为与第二个菜单项相关联,这样顺序进行,直到完成所有菜单项。

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

说明:可以手工或用ClassWizard来给菜单项添加UPDATE_COMMAND_UI消息响应,利用响应函数中传进来的CCmdUI对象指针可完成设置菜单项可使用,不可使用,变灰,设置标记菜单等操作。

 

9,如果要想让工具栏上的某个图标与菜单项的某个菜单相关联,只需要将图标的ID设置为该菜单项的ID。

工具栏图标的索引记数顺序是:从左至右从0开始,分隔符也算索引号。

要同时操作菜单栏和工具栏建议用ID而不要用索引号,索引号可能出错

 

10.

void CMainFrame::OnUpdateEditCut(CCmdUI*pCmdUI)

{

 if(2==pCmdUI->m_nIndex)

 pCmdUI->Enable();//当此菜单显示时,设为可用。

}

 

11.右键弹出菜单功能的实现方法有两个:

 a.Project->Add to Project->component and controls->文件夹VC components->Popup Menu OK

  b.用TrackPopupMenu()实现。

 CMenu menu;

 menu.LoadMenu(IDR_MENU1);

 CMenu *pPopup=menu.GetSubMenu(0);

 ClientToScreen(&point);

 pPopup->TrackPopupMenu(TPM_LEFTALIGN |TPM_RIGHTBUTTON, point.x, point.y,

  GetParent());

 

12动态创建菜单的方法: CMenu menu;

 menu.CreatePopupMenu();

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

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

 menu.AppendMenu(MF_STRING,IDM_HELLO,"Hello");

 menu.AppendMenu(MF_STRING,112,"Weixin");

 menu.AppendMenu(MF_STRING,113,"Mybole");

 menu.Detach();

 GetMenu()->GetSubMenu(0)->AppendMenu(MF_STRING,114,"Welcome");

 GetMenu()->GetSubMenu(0)->InsertMenu(ID_FILE_OPEN,

  MF_BYCOMMAND | MF_STRING,115,"维新");

//GetMenu()->DeleteMenu(1,MF_BYPOSITION);

//GetMenu()->GetSubMenu(0)->DeleteMenu(2,MF_BYPOSITION);

 

13.为动态创建的菜单增加消息响应的步骤

  a.在resource.h中增加#define IDM_HELLO 123

  b.在MainFrm.h中加入afx_msg void OnHello();

 c.MainFrm.cpp中加入ON_COMMAND(IDM_HELLO,OnHello)

  d.最后加入

void CMainFrame::OnHello()

{

 MessageBox("Hello!");

}

 

14.动态增加电话号码本步骤

  a.处理WM_Char消息。如果回车,则清空字符串,窗口重绘invalidate,将人名加入到菜单中,将字符串保存集合类CStringArray中,用的是成员函数Add方法。

  b.取出动态创建的菜单的数据的方法。

    1)创建一个弹出菜单,弹出菜单下面有4个子菜单。将子菜单的ID号连续。

    2)在resource.h中添加#define IDM_PHONE1 123....

    3)添加其消息响应函数。注意注释中的文字

BEGIN_MESSAGE_MAP(CMenu2View, CView)

 //{{AFX_MSG_MAP(CMenu2View)

 ON_WM_CHAR()

 ON_COMMAND(ID_EDIT_COPY, OnEditCopy)//下面的4句代码原来在此处。

 //}}AFX_MSG_MAP

 // Standardprinting commands

 ON_COMMAND(IDM_PHONE1, OnPhone1)//一定要这4句代码移到此处。

 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()

4)填写代码

 

15,CWnd::DrawMenuBar重画菜单栏

//Redraws the menu bar. If a menu bar is changedafter Windows has created the window, call this function to draw the changedmenu bar

注意View类没有菜单栏,只有框架类有

CWnd::GetParent //get a pointer to a child window’sparent window (if any).

CWnd::Invalidate //注意其参数的作用,true背景会被擦除,false背景不背擦除

 

16,如何在MainFrame中拦截OnCommand消息?答,在它增加OnCommand的消息处理函数即可。

命令消息是到OnCommand函数的时候完成路由的。

由于CWnd::OnCommand是个虚函数,可以在框架类中重写OnCommand函数,从而可以截获菜单消息使它不再往下(VIEW类)路由。

例:

BOOL CMainFrame::OnCommand(WPARAM wParam,LPARAM lParam)

{

 //TODO: Add your specialized code here and/or call the base class

 intMenuCmdId=LOWORD(wParam);//取命令ID

 CMenu2View*pView=(CMenu2View*)GetActiveView();//获取当前VIEW类指针

 if(MenuCmdId>=IDM_PHONE1 &&MenuCmdId<IDM_PHONE1+pView->m_strArray.GetSize())//消息范围判断

 {

 CClientDC dc(pView);

 dc.TextOut(0,0,pView->m_strArray.GetAt(MenuCmdId-IDM_PHONE1));

 return TRUE;

   //函数返回,避免调用CFrameWnd::OnCommand函数,在CFrameWnd::OnCommand中截获的消息会交由VIEW类处理

 }

 return CFrameWnd::OnCommand(wParam, lParam);

 //调用基类OnCommand函数,在CFrameWnd::OnCommand中截获的消息会交由VIEW类处理

}

 

17.错误调试方法:Missing ";" before "*"

CMenu2Doc*GetDocument();//因为CMenu2Doc是个不认识的变量,将其头文件包含进即可。

你可能感兴趣的:(第6课菜单)