有关菜单的操作主要用到CMenu类,当然也可用相应API函数,CMenu类只是MFC对API中操作菜单的函数的封装而已。 不过能用类就尽量用类,类的组织方式好呗,代码看着也舒服。 若是SDK编程,那就用API吧 。
CMenu menuMain,menu1; //首先 定义CMenu对象
1. 用LoadMenu函数从资源加载
menuMain.LoadMenu(IDR_MAINFRAME); //从资源加载,这里使用SDI的主菜单资源
2. 用CreateMenu函数创建
menu1.CreateMenu(); //创建菜单,还没有菜单项
// ID_TEST1 在Resource.h 中定义,随便给个整数值,不要和已有的重复就行了
menu1.AppendMenu(MF_STRING,ID_TEST1,"Test1"); // 第一项菜单项
menu1.AppendMenu(MF_STRING,ID_TEST2,"Test2"); // 第二项菜单项
menu1.InsertMenu(1,MF_BYPOSITION|MF_STRING,
(UINT)ID_TEST1,"ID_TEST1"); // 在第二项菜单项前添加新菜单项
同样用AppendMenu()、InsertMenu()函数。不过要注意参数的设置。
menu1.AppendMenu(MF_BYPOSITION|MF_POPUP|MF_STRING,
(UINT) menuMain.GetSubMenu(0) ->m_hMenu,"子菜单");
//第二个参数是菜单的句柄HMENU
用DeleteMenu()、RemoveMenu()函数来删除指定位置的菜单/菜单项。
两者区别:如果菜单项是一个弹出式菜单,那么DeleteMenu和RemoveMenu之间的区别就很重要。DeleteMenu清除弹出式菜单,但RemoveMenu不清除它。一个是彻底的删除,一个只是移除.
MSDN: 1.The DeleteMenu function destroys the handle to the menu or submenu and frees the memory used by the menu or submenu. 它使菜单或者子菜单的handle无效(destroys)。
2. RemoveMenu does not destroy the menu or its handle, allowing the menu to be reused. 可以再利用,并不从内存中将menu删除。
CMenu menu1;
menu1.CreatePopupMenu(); //动态创建弹出式菜单对象
menu1.AppendMenu(MF_STRING,ID_TEST1," 菜单项1");
menu1.AppendMenu(MF_STRING,ID_TEST2," 菜单项2");
menu1.InsertMenu(2,MF_BYPOSITION|MF_POPUP|MF_STRING,
(UINT) menuMain.m_hMenu,"子菜单"); //添加子菜单
CPoint pt;
GetCursorPos(&pt);
menu1.TrackPopupMenu(TPM_RIGHTBUTTON, pt.x, pt.y, this);
menu1.DestroyMenu();
1. 若是资源中添加的菜单可用Class Wizard添加菜单的响应事件。
2. 若是通过代码创建的菜单,要手工实现菜单的消息映射。本例是在CmainFrame类中,当然也可在View类、Doc类中,基于对话框的同样也可以。
1) 在.h文件中
// Generated message map functions
protected:
//{{AFX_MSG(CMainFrame)
afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
afx_msg void OnChangmenuitem(); //这里添加菜单命令处理函数的声明
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
2) 在.cpp文件中,
BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
//{{AFX_MSG_MAP(CMainFrame)
ON_WM_CREATE()
ON_COMMAND(IDM_CHANGMENUITEM, OnChangmenuitem) //这里添加,注意没有’ ;’
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
void CMainFrame::OnChangmenuitem()
{
// 这里写你要如何处理的代码
……
}
其他方法:
若菜单ID值是连续的,最好用ON_COMMAND_RANGE来映射消息处理函数,可以在一个函数中处理一个范围内的所有消息。
当用户按下某个菜单项,会发出一个WM_COMMAND消息,而菜单项的ID号,就包含在参数wParam的低位中.
BOOL CYourView::OnCommand(WPARAM wParam, LPARAM lParam)
{
// TODO: Add your specialized code here and/or call the base class
UINT m_nItemID=LOWORD(wParam);
if (m_nItemID==ID_YOURITEM) //ID_YOURITEM为你加入菜单项时指定的ID号
{
//在这里放入响应的代码
}
return CScrollView::OnCommand(wParam, lParam);
}
对于右键菜单可以通过TrackPopupMenu的返回值来处理。在参数uFlags中设置TPM_ RETURNCMD,这样返回值就是你选择的菜单项的ID,然后可以根据ID来处理。
TrackPopupMenu(TPM_ RETURNCMD ,pt.x,pt.y,this);
MSDN:If you specify TPM_RETURNCMD in the uFlags parameter, the return value is the menu-item identifier of the item that the user selected.
DrawMenuBar () ; //当您改变菜单时,需要重画菜单才能显示所做的改变
GetSystemMenu () ; //取得窗口控制窗口
GetMenu() //取得当前程序使用的菜单
GetSubMenu() //取得子菜单
应使用CMenu类的Detach()成员函数从Cmenu对象中分离出菜单句柄,避免对象失效后程序出错。
如:
CMenu menu;
menu.CreatePopupMenu(); //动态创建弹出式菜单对象
menu.AppendMenu(0,ID_TEST1,"Test1");
menu.AppendMenu(0,ID_TEST2,"Test2");
CMenu* menuMain = GetMenu(); //取得程序主菜单 需在CMainFrame类中
menuMain->AppendMenu(MF_BYPOSITION|MF_POPUP|MF_STRING,(UINT)menu.m_hMenu,"子菜单1");
menu.Detach(); //直接用menu.m_hMenu在运行时出错,menu对象在这个事件结束就销毁了
DrawMenuBar();
1、弹出式菜单,就叫PopUP Menu
2、菜单命令的路由是:View->Doc->MainFrm->App
//-------------------------------------华丽的分割线--------------------------------------------------//
3、GetMenu()获取菜单栏指针,一般括号中无参数
4、GetSubMenu()获取菜单列的指针
一般过程: 菜单栏指针(GetMenu())---->菜单列指针(GetSubMenu())---->单独向操作
5、EnableMenuItem()函数可以作为调整菜单项是否可用的函数,但是要在CMainFrm的构造函数里,令m_AutoMenuEnable=FALSE;即可
//-------------------------------------华丽的分割线--------------------------------------------------//
6、对于菜单项单独来说,都有UPDATE_COMMAND_UI消息来设置单项的一些属性,如菜单项是否可用,设置缺省菜单项,菜单项打勾等操作;
类似于每一个窗体都有一个m_hWnd对象一样,菜单中的pCmdUI指针,都有一个m_nID或者m_nIndex的索引
菜单的操作包括了对菜单和菜单项的操作。
//---------------------------------------分割线----------------------------------------------------//
7、判断一个事件是不是第一次,可以在这个类中,定义一个int变量(比如为index),并赋值为-1,然后在函数中
if (0==++index) {...}
即可
8、要注意,动态添加的菜单因为其生命周期的原因,要用函数menu.Detach()函数,这样,这个菜单对象的局部生命周期结束时,不会去销毁一个它 不具有权的菜单
//---------------------------------------分割线----------------------------------------------------//
VS2010中,编写右键弹出式菜单,请见:http://www.cnblogs.com/52yixin/articles/2105851.html
下面是集中右键弹出菜单的边写源代码:
第一种:静态的,这种一般是在Resource里面,把需要右键弹出的菜单做好了,然后在这里,用一个菜单类对象来LoadMenu(菜单ID)来读取,然后对象.GetSubMenu(0)->TrackPopupMenu(...)即可建立,注意参数即可。
1 void Cself_5View::OnContextMenu(CWnd* pWnd, CPoint point) 2 { 3 // TODO: Add your message handler code here 4 //---------------------下面是静态创建菜单------------------------ 5 CMenu staticMenu; 6 staticMenu.LoadMenu(IDR_MENU1); 7 8 staticMenu.GetSubMenu(0)->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTALIGN,point.x,point.y,this,NULL); }
第二种:动态添加。就是说,写在代码里面。这里先用一个菜单类对象goMenu来CreatePopupMenu(),注意是创建一个弹出式菜单。然后,在下面,就用AppendMenu()函数来往这个“菜单”里边,不断的补充项目,如下面的“子菜单1”到“子菜单4”
这里要注意的是AppendMenu()的两种基本菜单插入式操作,它是用来插在最后的。中间那个句柄,是表示要插在哪一个菜单后面的,注意前面有一个强制转换(UINT)
1 void Cself_5View::OnContextMenu(CWnd* pWnd, CPoint point) 2 { 3 // TODO: Add your message handler code here 4 //---------------------下面是动态创建菜单------------------------ 5 CMenu goMenu; 6 goMenu.CreatePopupMenu(); 7 goMenu.AppendMenu(MF_ENABLED, (UINT)goMenu.m_hMenu, "子菜单1 "); 8 goMenu.AppendMenu(MF_ENABLED, (UINT)goMenu.m_hMenu, "子菜单2 "); 9 goMenu.AppendMenu(MF_ENABLED, (UINT)goMenu.m_hMenu, "子菜单3 "); 10 goMenu.AppendMenu(MF_ENABLED, (UINT)goMenu.m_hMenu, "子菜单4 "); 11 goMenu.TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTALIGN,point.x,point.y,this,NULL); }
第三种:是右键弹出二级子菜单。这里首先定义了两个菜单类对象doubleMenu1和 doubleMenu2。然后二者分别CreatePopupMenu()创建了弹出式菜单,然后用AppendMenu()往doubleMenu2“补充”了两条项目。然后重点在于doubleMenu1.AppendMenu那里,中间的句柄传递的是doubleMenu2的,然后最后用TrankPopupMenu创建了一个右键弹出式菜单
注意代码中有一个InsertMenu(),道理同AppendMenu()
不是MF_STRING就要注意中间那个参数,传递菜单句柄。每一个菜单,都有一个m_hMenu
1 void Cself_5View::OnContextMenu(CWnd* pWnd, CPoint point) 2 { 3 // TODO: Add your message handler code here 4 //---------------------下面是创建右键二级菜单------------------------ 5 CMenu doubleMenu1,doubleMenu2; 6 doubleMenu1.CreatePopupMenu(); 7 doubleMenu2.CreatePopupMenu(); 8 char one[]="item1"; 9 char two[]="itme2"; 10 doubleMenu2.AppendMenu(MF_STRING,0,one); 11 doubleMenu2.AppendMenu(MF_STRING,0,two); 12 doubleMenu2.InsertMenu(1,MF_STRING | MF_BYPOSITION,(UINT)doubleMenu2.m_hMenu,"yes"); 13 14 doubleMenu1.AppendMenu(MF_POPUP,(UINT)doubleMenu2.m_hMenu,"一级子菜单"); 15 16 doubleMenu1.TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTALIGN,point.x,point.y,this,NULL); 17 }
//--------------------------------------不管你信不信 这都是一条分割线-------------------------------------------------------//
如果要用代码动态的在MainFrm中添加一个菜单项的话,那么需要添加一个DrawMenuBar()函数,作为完成菜单栏的重绘操作。要注意用CMainFrm类调用,因为菜单栏属于是CMainFrm类的
学会使用集合类~!
//--------------------------------------不管你信不信 这都是一条分割线-------------------------------------------------------//
下面这个重载的OnCommand函数要充分的学习,它是捕获命令消息的函数,我们可以重载它,作为一个虚函数来添加
1 BOOL CMainFrame::OnCommand(WPARAM wParam, LPARAM lParam) 2 { 3 // TODO: Add your specialized code here and/or call the base class 4 int MenuCmdId=LOWORD(wParam); 5 Cself_5View *pView=(Cself_5View*)GetActiveView(); 6 if(MenuCmdId>=IDM_PHONE1 && MenuCmdId<IDM_PHONE1+pView->m_strArray.GetSize()) 7 { 8 CClientDC dc(pView); 9 dc.TextOut(0,0,pView->m_strArray.GetAt(MenuCmdId-IDM_PHONE1)); 10 return TRUE; 11 } 12 return CFrameWnd::OnCommand(wParam, lParam); 13 }
注意,在框架类中添加View类的成员变量。注意这一句:
Cself_5View *pView=(Cself_5View*)GetActiveView();
定义了一个View类的指针,并用GetActiveView()获取当前VIEW类指针,注意这个GetActiveXXXX还有好几个
然后以此来调用别的类的成员变量!!这算是一个知识点!!
然后是
OnCommand(WPARAM wParam, LPARAM lParam) 中的wParam参数的低字节,是菜单项、工具按钮或者加速键的命令ID,所以我们用 LOWORD(wParam) 去获取