Windows程序设计__孙鑫C++Lesson6《菜单操作》

Windows程序设计__孙鑫C++Lesson6《菜单操作》

本节要点
1.消息响应顺序
2.消息分类
3.菜单结构与特点
4.静态菜单的操作
5.动态创建菜单的操作
6.电话本实现

//**************************************************************************************************************
1.消息响应顺序
同时在四个类中添加Test类的响应函数菜单项响应顺序:View类 Doc类 CMainFrame类 App类
2.消息分类
标准消息:除了WM_COMMAND之外,所有以WM_开头的消息;从CWnd派生的类都可以接受这类消息如WM_CHAR ,也可以接受命令消息和通告消息。
命令消息:CCmdTarget派生的类如CWinApp类可以接受命令消息,但不可以接受标准消息.用ON_COMMAND宏映射的.
通告消息:控件产生的消息(如按钮单机)向其符窗口通知事件的发生;通知消息也是以WM_COMMAND形式呈现。CCmdTarget派生的类都可以接受这类消息。

总之,CCmdTarget派生的类都可以接受命令消息和通告消息;CWnd派生的类才可以接受标准消息,当然也可以接受命令消息和通告消息。
 消息分类也可参见下图:


3.菜单结构与特点
(1)子菜单:一整栋楼对应整个菜单栏,每个楼层对应菜单栏上一个子菜单(文件、编辑、查看等),房间对应每个子菜单的菜单项。
   菜单项:        找整栋楼-->找楼层--->找房间
          对应于   找整个菜单栏--->找子菜单--->找菜单项

菜单与楼栋的类比参见下图:


(2)索引访问菜单项不能忽略菜单分隔符,它在索引中也算一个值
(3)命令更新机制:
菜单项的维护是依赖于CN_UPDATE_COMMAND_UI小溪,谁捕获CN_UPDATE_COMMAND_UI消息,MFC就在其中创建一个CCmdUI对象(CCmdUI是一个没有基类的类)。
该对象与菜单项关联,调用该对象的成员函数DoUpdate().注意更新命令UI处理程序仅能应用于弹出式菜单项上的项目,不能应用于永久显示的顶级菜单项目。
(4)菜单中的索引和工具栏上的索引可能不一致,引起Enable的错误。
4.静态菜单的操作
(1)创建位图菜单 获取系统中菜单位图的大小,然后设定新建位图的大小为合适大小
//**********************************************************************************
 CString strMenuInfo;
 strMenuInfo.Format("x=%d,y=%d",GetSystemMetrics(SM_CXMENUCHECK),GetSystemMetrics(SM_CYMENUCHECK));
 MessageBox(strMenuInfo);
 m_bitChecked.LoadBitmap(IDB_BITMAP1);
 m_bitUnChecked.LoadBitmap(IDB_BITMAP2);
 pSuMenu->SetMenuItemBitmaps(2,MF_BYPOSITION,&m_bitChecked,&m_bitUnChecked);
//**********************************************************************************
(2)标记菜单
//**********************************************************************************
 //获取整个菜单或子菜单指针
 CMenu* pMenu=GetMenu();
 CMenu* pSuMenu=pMenu->GetSubMenu(0);
 //标记菜单
 //pSuMenu->CheckMenuItem(0,MF_BYPOSITION|MF_CHECKED);//位置索引
 //pSuMenu->CheckMenuItem(ID_FILE_NEW,MF_BYCOMMAND|MF_CHECKED);//ID索引
//**********************************************************************************
(3)缺省菜单  缺省菜单只能一个.
//**********************************************************************************
      //pSuMenu->SetDefaultItem(1,TRUE);//设置默认菜单项  通过位置计算
     // pSuMenu->SetDefaultItem(ID_FILE_OPEN);//设置默认菜单项 ID设定
//**********************************************************************************
(4)菜单不可用:注意设置 m_bAutoMenuEnable 为false,屏蔽系统的自动更新。
  执行m_bAutoMenuEnable =FALSE;后系统将不再判断哪些菜单可用或不可用,剪切复制菜单不再变灰.
(5)取消菜单  SetMenu(NULL);
(6)加载菜单
 SetMenu(NULL);
 CMenu menu;
 menu.LoadMenu(IDR_MAINFRAME);
 SetMenu(&menu);
     menu.Detach();//将局部菜单与对象断开  由窗口销毁时才析构菜单对象
(5)右键菜单
增加弹出菜单应该到View类而不是MainFrame类。
可以使用增加控件的方法,也可以用TrackPopupMenu函数自己写代码。
自己写代码时注意菜单显示位置为屏幕坐标为准而当前点击时以view类坐标为基准,那么屏幕坐标和客户区坐标就应该转换。坐标装换使用函数ClientToScreen();
弹出菜单的拥有者是View类,那么只能是View类响应弹出菜单项的命令;要想框架了响应应该用GetParent()获取框架指针。当使用GetParent()函数时依然是View类先响应,说明子窗口响应

优先级比父窗口高。
//*********************************************************************************************
void CMenuView::OnRButtonDown(UINT nFlags, CPoint point)
{
 // TODO: Add your message handler code here and/or call default
 CMenu menu;
 menu.LoadMenu(IDR_MENU_POPUP);
 CMenu *pMenu=menu.GetSubMenu(0);
 //CRect myRect;
     //GetClientRect(&myRect);
     ClientToScreen(&point);//坐标转换
     pMenu->TrackPopupMenu(TPM_RIGHTALIGN,point.x,point.y,this,NULL);
 CView::OnRButtonDown(nFlags, point);
}
//*********************************************************************************************
5.动态创建菜单的操作
1.子菜单或菜单项的追加、插入、删除
//*********************************************************
 CMenu menu;
 menu.CreatePopupMenu();//创建PopUpMenu
 //GetMenu()->AppendMenu(MF_POPUP|MF_ENABLED|MF_STRING,UINT(menu.m_hMenu),"AppendMenu");//添加弹出菜单到主菜单
 GetMenu()->InsertMenu(2,MF_BYPOSITION|MF_POPUP|MF_STRING,UINT(menu.m_hMenu),"AppendMenu");
    menu.AppendMenu(MF_STRING,IDM_MENUTEST,"Item1");
 menu.AppendMenu(MF_STRING,112,"Item2");
 menu.AppendMenu(MF_STRING,113,"Item3");//添加菜单项到弹出菜单
 menu.Detach();//不要忘记与menu对象分离
 GetMenu()->GetSubMenu(0)->AppendMenu(MF_STRING,114,"ItemAppend");//在末尾追加菜单项
     GetMenu()->GetSubMenu(0)->InsertMenu(1,MF_BYPOSITION|MF_STRING,115,"ItemInsert");//指定位置插入菜单项
 GetMenu()->DeleteMenu(1,MF_BYPOSITION);//删除子菜单
     GetMenu()->GetSubMenu(0)->DeleteMenu(2,MF_BYPOSITION);//删除菜单项
//*********************************************************
(2)动态添加的菜单,命令消息的响应
 添加ID
 添加命令消息响应原型
 添加消息映射宏
 添加消息响应函数
6.电话本实现
(1)思路:添加菜单并重画菜单栏 调用DrawMenuBar()并且注意是利用CMainFrame指针调。
     添加字符输入处理WM_CHAR  消息
     重绘窗口Invalidate()
     CStringArray实现输入保存(CObArray使用类同).
(2)error C2501: 'CMenu2Doc' : missing storage-class or type specifiers错误解决方法:包含Menu2Doc.h到Menu2View.h
(3)命令消息的路由:
CWnd::WindowProc--->OnWndMsg--->判断消息 标准消息则通过消息映射处理;如果是命令消息交由OnCommand处理,通告消息交由OnNotify处理.最后都交由OnCmdMsg处理。本程序

CMainFrame-->子窗口View类--->Doc类-->文档类查看消息映射,没有则把消息交回给View-->View类查看消息映射,没有则把消息交回给交回CmainFrame--->CMainFrame类查看自己有没有响

应该消息,没有则交给CMenuApp;

命令消息路由过程参见下图:

 

为了加深理解课参看以下代码:
//*********************************************************
//WINCORE.CPP
virtual LRESULT CWnd::WindowProc(UINT message, WPARAM wParam, LPARAM lParam);
virtual BOOL  CWnd::OnWndMsg(UINT message, WPARAM wParam, LPARAM lParam,LRESULT* pResult);
virtual BOOL  CWnd::OnCommand(WPARAM wParam, LPARAM lParam);
virtual BOOL  CWnd::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult);

LRESULT CWnd::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{
 // OnWndMsg does most of the work, except for DefWindowProc call
 LRESULT lResult = 0;
 if (!OnWndMsg(message, wParam, lParam, &lResult))
  lResult = DefWindowProc(message, wParam, lParam);
 return lResult;
}
BOOL CWnd::OnWndMsg(UINT message, WPARAM wParam, LPARAM lParam, LRESULT* pResult)
{
 LRESULT lResult = 0;

 // special case for commands
 if (message == WM_COMMAND)
 {
  if (OnCommand(wParam, lParam))
  {
   lResult = 1;
   goto LReturnTrue;
  }
  return FALSE;
 }

 // special case for notifies
 if (message == WM_NOTIFY)
 {
  NMHDR* pNMHDR = (NMHDR*)lParam;
  if (pNMHDR->hwndFrom != NULL && OnNotify(wParam, lParam, &lResult))
   goto LReturnTrue;
  return FALSE;
 }
   //***后面省略****
}
//*********************************************************
virtual BOOL CFrameWnd::OnCommand(WPARAM wParam, LPARAM lParam);
由此可见在CMainFrame添加virtual BOOL OnCommand()函数则MFC先调用MainFrame的OnCommand函数,
然后再调用基类的OnCommand()函数,这个过程中好像MainFrame先截获了命令消息而View类则无法捕获了。
(4)电话本实现
成员变量:
CMenu2View 类中
public:
    CStringArray m_strArray;//保存输入
private:
  int m_nIndex;
  CMenu m_menu;
  CString m_strPhone;
//*********************************************************
//输入实现
void CMenu2View::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
{
 // TODO: Add your message handler code here and/or call default
 CClientDC dc(this);
 if(0x0d==nChar)
 {
        if(0==m_nIndex)
  {
   m_menu.CreatePopupMenu();
      GetParent()->GetMenu()->AppendMenu(MF_POPUP|MF_ENABLED|MF_STRING,UINT(m_menu.m_hMenu),"PhoneBook");
   GetParent()->DrawMenuBar();//重绘菜单栏 立即更新
  }
  //利用输入信息 动态添加菜单项
  m_menu.AppendMenu(MF_STRING,IDM_PHONE1+m_nIndex,m_strPhone.Left( m_strPhone.Find(' ') ) );
  Invalidate();
  m_strArray.Add(m_strPhone);
  m_nIndex++;
  m_strPhone.Empty();
 }
 else
 {
  m_strPhone+=nChar;
     dc.TextOut(0,0,m_strPhone);
 }
 CView::OnChar(nChar, nRepCnt, nFlags);
}
//*********************************************************
//输出实现
BOOL CMainFrame::OnCommand(WPARAM wParam, LPARAM lParam)
{
 // TODO: Add your specialized code here and/or call the base class
 //主框架消息截获(利用消息路由机制)
 int MenuID=LOWORD(wParam);
 CMenu2View *pView=(CMenu2View *)GetActiveView();
 if(MenuID>=IDM_PHONE1  && MenuID<( IDM_PHONE1 +pView->m_strArray.GetSize()) )
  {
  CClientDC dc(pView);
  dc.TextOut(0,0,pView->m_strArray.GetAt(MenuID-IDM_PHONE1));
  MessageBox("MainFrame Responsed!");
     return true;
  }
 return CFrameWnd::OnCommand(wParam, lParam);
}
//*********************************************************

程序运行效果如下图:


本节小结:
1.消息分类
2.消息路由
3.静态菜单和动态创建菜单的操作

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值