五. 菜单编程--Windows编程课程学习笔记

5.1 菜单命令响应函数

在资源视图中,Menu下的IDR_MAINFRAME是一个默认菜单资源,可以在此基础上继续添加菜单项。

MFC中,设置为Pop-up类型的菜单称为弹出式菜单,VC++默认顶层菜单为弹出式菜单,这种菜单不能响应命令。将菜单的属性对话框中的Pop-up选项去掉,该菜单成为一个菜单项,对应有一个ID号,可以响应命令。

添加一个新的菜单项,将Popup改成false,ID号为ID_TEST。利用类向导的方法为其在主框架类添加响应函数(在命令选项卡,找ID_TEST对象)。添加一个消息框弹出显示”MainFrame Clicked”的命令。

void CMainFrame::OnTest()
{
    // TODO: 在此添加命令处理程序代码
    MessageBox("MainFrame Clicked");
}

5.2 菜单命令的路由

5.2.1程序类对菜单命令的相应顺序

       响应Test菜单项命令的顺序依次是:视类、文档类、框架类、应用程序类

5.2.2 Windows消息的分类

1)标准消息

除WM_COMMAND之外,所有以WM_开头的消息。从CWnd派生的类,都可以接收到这类消息。

2)命令消息

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

3)通告消息

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

       CWnd派生于CCmdTarget类。凡是从CWnd派生的类,它们既可以接受标准消息,也可以接受命令消息和通告消息。CCmdTarget派生的类,只能接受命令消息和通告消息。

5.2.3  菜单命令的路由

1)当点击某菜单项时,最先接收到这个菜单命令消息的是框架类

2)框架类把接收到的这个消息传给它的子窗口,即视类。视类根据命令消息映射机制查找自身是否对这个消息进行了响应,如果响应了,则调用自身相应响应函数

3)如果视类没有对此命令消息作出响应,就交由文档类,文档类同样查找自身是否这个消息进行了响应,如果响应了,则调用自身相应响应函数。

4)如果文档类也未做出响应,就把这个命令消息交还给视类,后者再交还给框架类

5)框架类查看自己是否对这个命令消息进行了响应,如果它也没有相应,就把这个菜单命令消息交给应用程序类,由后者来处理。

5.3基本菜单操作

子菜单和菜单项的概念类比于楼层和房间号。

5.3.1 标记菜单

为【文件】子菜单的【新建】菜单项添加标记。

1)获得【文件】子菜单的【新建】菜单项。

①首先获得菜单栏GetMenu(CWnd成员函数);

②获得子菜单GetSubMenu(CMenu成员函数),有一个参数nPos指定了子菜单的索引号;

③UNIT CheckMenuItem(UINT nIDCheckItem, UINT nCheck)可以为菜单项添加(移除)一个标。

int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
…
    // 根据位置,计算索引时分隔符也算
    GetMenu()->GetSubMenu(0)->CheckMenuItem(0,MF_BYPOSITION | MF_CHECKED);
    //ID号
    GetMenu()->GetSubMenu(0)->CheckMenuItem(ID_FILE_NEW, MF_CHECKED);
    return 0;
}

5.3.2 默认菜单项

       子菜单下有一个菜单项是粗体形式显示的,就是该子菜单的默认菜单项。可以用CMenu类的SetDefaultItem函数实现。一个子菜单只能有一个默认菜单项。

    BOOLSetDefaultItem(UINT uItem, Bool fByPos = FALSE);
    GetMenu()->GetSubMenu(0)->SetDefaultItem(1,TRUE);
    GetMenu()->GetSubMenu(0)->SetDefaultItem(ID_FILE_OPEN);

5.3.3 图形标记菜单

       子菜单下用图形标记。可以用CMenu类的SetMenuItemBitmaps函数实现。nPosition由nFlag决定,MF_BYCOMMAND则第一个为菜单项标识,MF_BYPOSITION则为索引。pBmpUnchecked取消时位图,pBmpChecked选定时位图。位图可以为私有成员变量,也可以局部变量Detach。

    UNITSetMenuItemBitmaps(UINT nPosition, UINT nFlag, const CBitmap* pBmpUnchecked,const CBitmap* pBmpChecked)
    CBitmap bmp1, bmp2;
    bmp1.LoadBitmap(IDB_BITMAP1);
    bmp2.LoadBitmap(IDB_BITMAP2);
    GetMenu()->GetSubMenu(0)->SetMenuItemBitmaps(0,MF_BYPOSITION, &bmp1, &bmp2);
    bmp1.Detach();
    bmp2.Detach();

       可以用GetSystemMetrics函数获得图形标记菜单上显示的位图尺寸。因为VC6.0++只能显示局部图形,而VS2015中可以根据图形尺寸调整显示。

    CString str;
    str.Format("x = %d, y = %d", GetSystemMetrics(SM_CXMENUCHECK), GetSystemMetrics(SM_CYMENUCHECK));
    MessageBox(str);

5.3.4 禁用菜单项

       实现使用禁用或变灰显示。nIDEnableItem由nEnable决定。菜单的禁用状态和变灰状态是不同的。通常把MF_GRAYED和MF_DISABLED这两个标志放在一起使用。该函数要生效,必须在CMainFrame类的构造函数中把成员变量m_bAutoMenuEnable设置为FALSE,不会对ON_UPDATE_COMMAND_UI和ON_COMMAND消息进行响应了,但同时【编辑】子菜单不再以灰色显示。要使用菜单命令更新机制,则该变量应设置为TRUE(缺省值)。

    UNIT EnableMenuItem(UINT nIDEnableItem, UINTnEnable)
    GetMenu()->GetSubMenu(0)->EnableMenuItem(3,MF_BYPOSITION | MF_GRAYED | MF_DISABLED);

5.3.5 移除和装载菜单

       移除一个菜单可以利用CMenu类的SetMenu函数。BOOL SetMenu(CMenu * pMenu);加载菜单可以利用CMenu类的LoadMenu函数。如果CMenu对象是一个临时对象,则在加载完成之后必须加上menu.Detach()。Detach会把菜单句柄与这个菜单对象分离,这样,当这个局部对象的生命周期结束时,它不会去销毁一个它不再具有拥有权的菜单资源。

    SetMenu(NULL);
 
    CMenu menu;
    menu.LoadMenu(IDR_MAINFRAME);
    SetMenu(&menu);
    menu.Detach();

5.3.6 MFC菜单命令更新机制

菜单项状态的维护是依赖于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处理程序仅应用于弹出式菜单项上的项目(有ID号),不能应用于顶层菜单项目(无ID号)。

       工具栏上的一个工具按钮与菜单栏中的某个菜单项相关联,只要他们的ID设置为一个标识就可以。菜单栏和工具按钮的位置索引计算方式不同,最好采用菜单项标识或工具栏按钮标识的方式来进行设置。

5.3.7 快捷菜单

1) 为Menu程序增加一个新的菜单资源。在ResouceView上的Menu分支上单击鼠标右件,选择“Insert Menu”命令,为这个菜单资源添加菜单项。由于在显示快捷菜单时顶级菜单不出现,所以可以给它设置任意的文本。

2)给视类添加WM_RBUTTONDOWN消息响应函数。加载菜单资源到CMenu对象

3)调用TrackPopupMenu函数。BOOL TrackPopupMenu(NUINT nFlags, int x, int y, CWnd* pWnd, LPSCECTlpRect = NULL); nFlags为指定菜单显示位置,xy为坐标,pWnd指定拥有者,lpRect指定矩形区域,若在此区域点击不显示。将鼠标点的客户去坐标转换为屏幕坐标。ClientToScreen

void CMyMFCAppView::OnRButtonDown(UINT nFlags, CPoint point)
{
    //TODO: 在此添加消息处理程序代码和/或调用默认值
    CMenu menu;
    menu.LoadMenu(IDR_MENU1);
    CMenu* pPopup = menu.GetSubMenu(0);
    ClientToScreen(&point);
    pPopup->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, point.x, point.y, this);
 
    CView::OnRButtonDown(nFlags, point);
}

4)利用类向导添加响应函数。选择COMMAND消息。在视类和主框架类分别响应。可以看到出现View show字符串的消息框。对于快捷菜单,如果将其拥有者窗口设置为框架类窗口,则框架类窗口才能有机会获得对该快捷菜单中的菜单项的命令响应,否则,就只能有视类窗口作出响应。

void CMyMFCAppView::OnShow()
{
    //TODO: 在此添加命令处理程序代码
    MessageBox("Viewshow!");
}
 
void CMainFrame::OnShow()
{
    //TODO: 在此添加命令处理程序代码
    MessageBox("Frameshow!");
}

5.4 动态菜单操作

5.4.1 添加菜单项目

1)添加顶层菜单:创建一个菜单对象, CreateMenu,之后添加一个菜单项目AppendMenu,BOOL AppendMenu (UINT nFlags, UINT_PTRnIDNewItem = 0, LPCTSTR lpszNewItem = NULL) nFlags状态信息,nIDNewItem取决于第一个,MF_POPUP则为一个顶层句柄,lpszNewItem取决于第一个。因为CMenu定义了一个局部变量,所以要Detach

int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
    …
    CMenumy_menu;
    my_menu.CreateMenu();
    GetMenu()->AppendMenu(MF_POPUP, (UINT)my_menu.m_hMenu,"my_menu");<span style="white-space:pre">	</span>DrawMenuBar();
    my_menu.Detach();
    …
}

2)添加顶层菜单下的菜单项:

GetMenu()->GetSubMenu(0)->AppendMenu(MF_POPUP, (UINT)my_menu.m_hMenu,"hello");

5.4.2 插入菜单项目

1)插入顶级菜单:InsertMenu函数实现。BOOL InsertMenu(UINT nPosition, UINT nFlags, UINT_PTR nIDNewItem = 0,LPCTSTR lpszNewItem = NULL);

    CMenu my_menu;
    my_menu.CreateMenu();
    //GetMenu()->AppendMenu(MF_POPUP,(UINT)my_menu.m_hMenu, "my_menu");
    GetMenu()->InsertMenu(2, MF_BYPOSITION | MF_POPUP, (UINT)my_menu.m_hMenu,"my_menu");
    my_menu.Detach();

2)插入顶级菜单下的菜单项

    GetMenu()->GetSubMenu(0)->InsertMenu(0, MF_STRING | MF_BYPOSITION,777, "Hello");

5.4.3 删除菜单

1)删除顶级菜单:BOOL DeleteMenu(UINTnPosition, UINT nFlags);

    GetMenu()->DeleteMenu(1, MF_BYPOSITION);<span style="white-space:pre">	</span>DrawMenuBar();

2)删除顶级菜单下的菜单项:

    GetMenu()->GetSubMenu(0)->DeleteMenu(0,MF_BYPOSITION);
    GetMenu()->GetSubMenu(0)->DeleteMenu(ID_FILE_NEW, MF_BYCOMMAND);

5.4.4 动态添加的菜单项的命令响应

遵循MFC的消息映射机制,需要手动添加三处代码来实现命令消息的响应。可先利用ClassWizard对程序中某个已有的静态菜单项添加命令消息响应,然后参照ClassWizard在程序中为其添加的内容,来完成动态菜单添加命令响应。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值