孙鑫vc第六课,菜单

一:创建静态菜单

  1.静态创建一个菜单是一种所见即所得的方式创建的,通过Resource ViewMenuIDR_MAINFRAME的方法创建。

    创建可弹出的子菜单时,菜单ID号为空,勾选Pop_up按钮,并且弹出菜单不能进行命令响应。

    对创建的子菜单进行命令响应,响应顺序为:CMenuViewCMenuDocCMainFrameCMenuApp。这和命令消息路由过程顺序是一样的。

    CMenuDocCMenuApp未从CWND派生,故响应函数中不能用MessageBox,要用应用程序框架类

AfxMessageBox

  2.消息分类:

    标准消息:

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

    命令消息:

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

    通告消息:

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

    CWndCCmdTarget类派生而来,故CWnd类可以接受所有的消息。

  3.命令消息路由:                                                                                  

                                                                                           OnCommand

AfxWndProcAfxCallWndProcWindowProcOnWndMsg                           OnCmdMsg

                                                                                            OnNotify     

OnCommand完成命令消息的路由,OnNotify完成由控件产生的的路由。

顺序为:CMainFrame首先交由View类,看是否有响应函数,若没有,传递给Doc类,

如果Doc类也没有的话返回给CMainFrame类,它检查自己是否有响应函数,没有的话再传递给App类进行消息路由。

   4.创建标记菜单(打√)

菜单的结构相当于楼房,楼层相当于子菜单,而房间相当于菜单项。

利用GetMenu()->GetSubMenu(0)->CheckMenuItem(0,MF_BYPOSITION | MF_CHECKED)

   5.缺省的菜单项(粗体显示)

一个子菜单中缺省菜单项只能有一个,分隔符也算一个索引位置。

   6.创建图形标记菜单

需要先获取位图尺寸。(GetSystemMetrics

所需函数为SetMenuItemBitmaps

Format函数时出现错误:代码如下

CString str;

    str.Format("d% ,d%",GetSystemMetrics(SM_CXMENUCHECK),GetSystemMetrics(SM_CYMENUCHECK));

          MessageBox(str);

终于找到答案了,原来是低级错误,是%d不是d%,呵呵。 

   7.使得菜单无效

EnableMenuItem

UINT EnableMenuItem( UINT nIDEnableItem, UINT nEnable );

nEnable

Specifies the action to take. It can be a combination of MF_DISABLED, MF_ENABLED, or MF_GRAYED, with MF_BYCOMMAND or MF_BYPOSITION. These values can be combined by using the bitwise OR operator. These values have the following meanings:

但是对应的菜单项仍旧可以使用,参考MSDN 有一注意点:

// 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.
但是改成FALSEMFC将不采用命令更新机制,所以原先哪些不能使用的菜单项现在将变为可以使用了。
8.取消菜单

SetMenu(NULL);

CMenu menu;

menu.LoadMenu(IDR_MAINFRAME);

SetMenu(&menu);

//BOOL SetMenu( CMenu* pMenu );
//pMenu
//Identifies the new menu. If this parameter is NULL, the current menu //is removed
这里会有个错误,当打印的时候。因为menu是个局部对象,有2中方法可以解决:①添加成员变量②调用Detach函数。

9.命令更新机制:

菜单项状态的维护是依赖于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号相同即可。

ToolBar中。

使用命令更新机制,如果是关联的话,菜单项与工具栏会一起变化,前提是用的是ID索引,因为用MF_BYPOSITION的话菜单项与工具栏的索引不是一一对应的。

void CMainFrame::OnUpdateFileOpen(CCmdUI* pCmdUI)

{

       // TODO: Add your command update UI handler code here

       if(ID_FILE_OPEN==pCmdUI->m_nID) //m_nID保存了与当前对象相关联的

              pCmdUI->Enable(false);          //ID        

}

还有就是m_nIndex保存的是与当前对象相关联的的索引值。

10.右键弹出菜单

通过ProjectAdd To ProjectComponents and ControlsPop-up Menu此方法在View类中添加了函数OnContextMenu

还可以同过捕获右键单击消息进行命令响应。对右键菜单的菜单项进行响应时,先响应View类,TrackPopupMenu(TPM_LEFTALIGN |  TPM_RIGHTBUTTON,point.x,point.y,GetParent(),NULL);函数TrackPopupMenu的第一个参数改变时貌似没什么反应,第四个参数如果是NULL,则只能响应View类的响应,而如果是GetPareent(),则可以响应MainFrame类。如果两个均有响应函数,则,率先响应view类的。

11.动态添加、插入、删除菜单:

动态添加菜单可在CMainFrameOnCreate里面进行,利用CMenuAppendMenu但注意It appends a new item to the end of a menu.如要添加至所需位置,则用InsertMenu

两者可混合使用,创建时要先CreatePopupMenu,然后再获得菜单栏指针进行插入,然后就可以直接用menu对象调用插入函数。

CMenu menu;

menu.CreatePopupMenu();

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

menu.Detach();//不要忘了Detach

如果是先获得GetMenu(),则插入的是一个子菜单,非菜单项,如要插入菜单项,则用menu直接调用,如menu.InsertMenu(1,MF_STRING,201,"Yao");

也可以对刚动态插入的菜单插入菜单项:GetMenu()->GetSubMenu(1)->AppendMenu(MF_STRING,202,"Lowary");

AppendMenu 的第二个参数Specifies either the command ID of the new menu item or, if nFlags is set to MF_POPUP, the menu handle (HMENU) of a pop-up menu. The nIDNewItem parameter is ignored (not needed) if nFlags is set to MF_SEPARATOR.网上说可以随便写不知道是不是这样。

    删除子菜单:

要删除一个子菜单,需要先获得该子菜单对象的指针,再调用DeleteMenu

为动态添加的菜单项添加命令响应:FileViewResource.h#define IDX_XXXX  XXX

然后手动添加响应代码,不会的可以通过MFC ClassWizard添加,然后效仿这个自己添加(3处),不过有一个问题afx_msg int OnMac();函数声明和函数响应对应的修饰符要一样。int CMainFrame::OnMac()

{

       MessageBox("Mac");

       return 1;

}

如果是int,则都为int 如果是void,则都是void

12.电话本程序

首先用View类捕获经键盘输入的消息,添加OnChar的命令响应。以下为代码

if(13==nChar)

       {

 

              if(0==++m_nindex)

              {

           // 第一次按Enter键,添加弹出按钮

                     m_menu.CreatePopupMenu();

                             GetParent()->GetMenu()->AppendMenu(MF_POPUP,(UINT)m_menu.m_hMenu,"Tel");//View类中添加子菜单时要先获取父类窗口的指针。

                     GetParent()->DrawMenuBar();//窗口重绘                 

              }        m_menu.AppendMenu(MF_STRING,IDM_PHONE1+m_nindex,m_strLine.Left(m_strLine.Find(' ')));//如果不是第一次,则添加菜单项。

              m_strArray.Add(m_strLine);

              m_strLine.Empty();

              Invalidate(); //将客户区粉刷

       }

       else

       {

              m_strLine+=nChar;

              dc.TextOut(0,0,m_strLine)//输出

       }

然后添加消息响应,可以通过静态添加,然后删除的方法,不过需要将消息映射那里的ON_COMMAND拿到宏外面,编译器检测到你添加的已删除他也会自动帮你删除的。添加消息响应时可以先将输入储存到CStringArray中,然后调用其函数。

13截获本应由View类响应的消息,改为由父类CMainFrame类响应。由于OnCommand是虚函数,所以可以添加OnCommand函数覆盖基类的OnCommand

virtual BOOL OnCommand(

   WPARAM wParam,

   LPARAM lParam

);

wParam

The low-order word of wParam identifies the command ID of the menu item, control, or accelerator. The high-order word of wParam specifies the notification message if the message is from a control. If the message is from an accelerator, the high-order word is 1. If the message is from a menu, the high-order word is 0.

所以需要取得低字节的两位,使用LOWORD函数。

int CommandId=LOWORD(wParam);

CMenu3View *pView=(CMenu3View*)GetActiveView();

if(CommandId>=IDM_PHONE1 && CommandId<IDM_PHONE1+pView->m_strArray.GetSize())

       {

              CClientDC dc(pView);

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

              //MessageBox("ok");

        return true;

         }

取得对应子类View的指针,用GetActiveView函数CFrameWnd::GetActiveView

Call this member function to obtain a pointer to the active view (if any) attached to a frame window (CFrameWnd).

CView* GetActiveView( ) const;

pView->m_strArray.GetSize()这个是为了使菜单具有更好的伸缩性。

return true;

使得命令消息不再向View类传递……

附:要添加级联菜单只要在该菜单项上勾选PopUp按钮就行。

Caption中添加&x,表示可以用助记键x来按下这个按钮,快捷键打开菜单响应在ResourceView中的Accelerator中的IDR_MAINFRAME中添加。

OVER

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值