六、菜单编程

一、基础知识

标准消息
      除WM_COMMAND之外,所有以WM_开头的消息。
      从CWnd派生的类,都可以接收到这类消息。
命令消息
      来自菜单、加速键或工具栏按钮的消息。这类消息都以WM_COMMAND呈现。在MFC中,通过菜单项的标识(ID)来区分不同的命令消息;在SDK中,通过消息的wParam参数识别。
 从CCmdTarget派生的类,都可以接收到这类消息。
通告消息
      由控件产生的消息,例如,按钮的单击,列表框的选择等均产生此类消息,为的是向其父窗口(通常是对话框)通知事件的发生。这类消息也是以WM_COMMAND形式呈现。
 从CCmdTarget派生的类,都可以接收到这类消息。

从CWND派生的类可接收标准消息和命令消息;从CCmdTarget派生的类可接收命令消息。

 

单文档应用程序处理消息的过程:

MainFrame接收到消息之后,首先将消息发送给view处理,view如果没有处理,就发送给doc处理,doc不能处理则返回给main处理,main不能处理就发送给app,最终由app处理。

 

MessageBox是CWND派生类的一个方法,不是CWND类没有这个方法,只能使用AfxMessageBox

 

为菜单项添加命令响应函数的方法:

在view--classwizard中,object下选择菜单项id,在message下选择command。添加命令响应函数。

 

二、菜单操作

1、标记菜单

在int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)中、

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

GetMenu()->GetSubMenu(0)->CheckMenuItem(ID_FILE_NEW,MF_BYCOMMAND | MF_CHECKED);

2、设置缺省菜单(粗体)

在int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)中如:

 GetMenu()->GetSubMenu(0)->SetDefaultItem(1,TRUE);
 GetMenu()->GetSubMenu(0)->SetDefaultItem(ID_FILE_OPEN);
 GetMenu()->GetSubMenu(0)->SetDefaultItem(5,TRUE);

缺省菜单只能有一项

3、创建图形标记菜单

m_bitmap.LoadBitmap(IDB_BITMAP1);

GetMenu()->GetSubMenu(0)->SetMenuItemBitmaps(0,MF_BYPOSITION,&m_bitmap,&m_bitmap);

 

4、让菜单项不能使用

首先在mainframe的构造函数中

m_bAutoMenuEnable=FALSE;

然后

GetMenu()->GetSubMenu(0)->EnableMenuItem(1,MF_BYPOSITION | MF_DISABLED | MF_GRAYED);

 

5、将整个菜单取消

SetMenu(NULL);

 

 

6、在5的基础上将菜单重新添加进来(动态添加菜单)

CMenu menu;
 menu.LoadMenu(IDR_MAINFRAME);
 SetMenu(&menu);
 menu.Detach();//将cmenu对象与句柄断开,否则menu对象应该设置成全局对象

 

 

三、命令更新机制

在view--classwizard中,object下选择菜单项id,在message下选择UPDATE_COMMAND_UI。添加命令响应函数。

1、

void CMainFrame::OnUpdateEditCut(CCmdUI* pCmdUI)
{
 // TODO: Add your command update UI handler code here
 if(2==pCmdUI->m_nIndex)
 //if(ID_EDIT_CUT==pCmdUI->m_nID)
  pCmdUI->Enable();//使得cut使能,这时不需要m_bAutoMenuEnable=FALSE;
}

2、

void CMainFrame::OnUpdateFileNew(CCmdUI* pCmdUI)
{
 // TODO: Add your command update UI handler code here
// if(ID_FILE_NEW==pCmdUI->m_nID)
//  pCmdUI->Enable(FALSE);
 if(0==pCmdUI->m_nIndex)
  pCmdUI->Enable(FALSE);//filenew不使能
}

 

 

四、右键弹出菜单

1、工程--添加到工程--components and contros--visual c++ components---popup menu

 

 

 

2、view中添加点击右键消息响应函数

void CMenuView::OnRButtonDown(UINT nFlags, CPoint point)
{
 // TODO: Add your message handler code here and/or call default
 CMenu menu;
 menu.LoadMenu(IDR_MENU1);//加载新创建的弹出菜单
 CMenu *pPopup=menu.GetSubMenu(0);
 ClientToScreen(&point);//将屏幕坐标转换成客户区坐标
 pPopup->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, point.x, point.y,
   GetParent());
 CView::OnRButtonDown(nFlags, point);
}

 

五、动态添加、删除、插入菜单

1、动态添加菜单

在int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)中

1)CMenu menu;

menu.CreatePopupMenu();//创建一个空的弹出菜单
GetMenu()->AppendMenu(MF_POPUP,(UINT)menu.m_hMenu,"WinSun");//将菜单append到现有菜单中

menu.Detach();//将句柄和cmenu对象断开

2)插入菜单

CMenu menu;

menu.CreatePopupMenu();//创建一个空的弹出菜单

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

menu.Detach();//将句柄和cmenu对象断开

3)插入菜单项

CMenu menu;

menu.CreatePopupMenu();//创建一个空的弹出菜单

GetMenu()->AppendMenu(MF_POPUP,(UINT)menu.m_hMenu,"WinSun");//将菜单append到现有菜单中

menu.AppendMenu(MF_STRING,IDM_HELLO,"Hello");
 menu.AppendMenu(MF_STRING,112,"Weixin");
 menu.AppendMenu(MF_STRING,113,"Mybole");

menu.Detach();//将句柄和cmenu对象断开

4)删除子菜单

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

 

2、动态添加的菜单的命令响应

1)在头文件中为子菜单项添加id号

fileview--headfile--resource.h中

#define IDM_HELLO     111

2)在mainframe中做命令响应函数原型

在头文件中的

protected:
 //{{AFX_MSG(CMainFrame)
 afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
 //}}AFX_MSG
 afx_msg void OnHello();//此处为原型声明
 DECLARE_MESSAGE_MAP()

3)在源文件中进行消息映射

BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
 //{{AFX_MSG_MAP(CMainFrame)
 ON_WM_CREATE()
 //}}AFX_MSG_MAP
 ON_COMMAND(IDM_HELLO,OnHello)//此处
END_MESSAGE_MAP()
4)编写消息响应函数

void CMainFrame::OnHello()
{
 MessageBox("Hello!");
}

 

 

 

六、电话本程序

功能描述:

1、在view视窗内输入人名和电话号码,人名和电话号码之间以空格分开。回车后将产生电话本菜单项,每一个菜单项以人名为caption;

2、点击菜单项时,将人名和电话号码显示出来。

 

实现步骤:

1、在view中增加WM_CHAR消息响应函数

void CMenu2View::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
{
 // TODO: Add your message handler code here and/or call default
 CClientDC dc(this);//创建dc
 if(0x0d==nChar)//如果输入的是回车键
 {
  if(0==++m_nIndex)//m_index初始值为-1,此处判断是否是第一次回车,如果是则创建弹出菜单,否则添加菜单项
  {
   m_menu.CreatePopupMenu();//创建空的弹出菜单
   GetParent()->GetMenu()->AppendMenu(MF_POPUP,
     (UINT)m_menu.m_hMenu,"PhoneBook");//将弹出菜单append到现有菜单
   GetParent()->DrawMenuBar();//使菜单发生重绘操作,立即显示
  }

//下面已经不是第一次回车了,所以append的是菜单项。第一个参数是表示产生菜单项,第二个参数是表示菜单项的索引值,第三项表示返回空格左边的字符作为菜单项的caption

//注:m_strLine保存的是“用户名 电话号码”,在fileview--resource.h中有如下定义:

#define IDM_PHONE1     201
#define IDM_PHONE2     202
#define IDM_PHONE3     203
#define IDM_PHONE4     204

///
  m_menu.AppendMenu(MF_STRING,IDM_PHONE1+m_nIndex,m_strLine.Left(m_strLine.Find(' ')));
  m_strArray.Add(m_strLine);//m_strArray是CStringArray类型的,它自动将m_strLine存入作为它的数组成员
  m_strLine.Empty();//每次都清空m_strLine,为接收下一字段做准备
  Invalidate();//使窗口发生重绘,这样回车之后原来输入的内容就消失了
  
 }
 else
 {
  m_strLine+=nChar;//不是回车键则为正常输入
  dc.TextOut(0,0,m_strLine);//将其显示出来
 }
 CView::OnChar(nChar, nRepCnt, nFlags);
}

2、为动态添加的菜单项添加命令响应函数

方法一:

首先创建新的menu菜单资源abc,添加菜单项1、2、3、4,然后通过classwizard为每个菜单项添加消息响应函数,注意ID号为IDM_PHONE1~IDM_PHONE4 。

然后将菜单资源删除,此时只留下了代码;

接着将view源文件中的消息映射移动到注释宏之外:

BEGIN_MESSAGE_MAP(CMenu2View, CView)
 //{{AFX_MSG_MAP(CMenu2View)
 ON_WM_CHAR()
 
 //}}AFX_MSG_MAP
 // Standard printing commands
 ON_COMMAND(IDM_PHONE1, OnPhone1)//如此所示
 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()

 

最后编辑消息响应函数

void CMenu2View::OnPhone1()
{
 // TODO: Add your command handler code here
 CClientDC dc(this);
 dc.TextOut(0,0,m_strArray.GetAt(0));
}

 

 

方法二:让cmainframe进行消息响应

1、在cmainframe中添加虚函数oncommand

BOOL CMainFrame::OnCommand(WPARAM wParam, LPARAM lParam)
{
 // TODO: Add your specialized code here and/or call the base class
 int MenuCmdId=LOWORD(wParam);//获得id号
 CMenu2View *pView=(CMenu2View*)GetActiveView();//获得当前视类指针
 if(MenuCmdId>=IDM_PHONE1 && MenuCmdId<IDM_PHONE1+pView->m_strArray.GetSize())//如果id号没有超出范围
 {
  CClientDC dc(pView);
  dc.TextOut(0,0,pView->m_strArray.GetAt(MenuCmdId-IDM_PHONE1));//显示出来
  //MessageBox("Test");
  return TRUE;//在此返回,这样就不去调用view的处理函数了
 }
 return CFrameWnd::OnCommand(wParam, lParam);
}

2、在mainframe源文件中:

#include "Menu2View.h"

 

3、将view源文件中的#include "Menu2Doc.h"放到view头文件中

 

 

后两步是为了避免编译错误。

 

 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值