依然是创建MFC单文档应用程序。
添加一个菜单选项:
右键属性,修改ID为IDM_TEST。//若无法修改,把属性--Popup修改为False即可。
点击--项目--类向导,分别在类名CMainFrame、CMenuApp、CMenuDoc、CMenuView,找到IDM_TEST,选择COMMAND消息--添加处理函数。
void CMainFrame::OnTest()
{
// TODO: 在此添加命令处理程序代码
MessageBox("MainFrame clicked");
}
void CMenuApp::OnTest()
{
// TODO: 在此添加命令处理程序代码
AfxMessageBox("App clicked"); //CMenuApp不是从CWin中派生出来的,所以使用afx全局函数
}
void CMenuDoc::OnTest()
{
// TODO: 在此添加命令处理程序代码
AfxMessageBox("Doc clicked");
}
void CMenuView::OnTest()
{
// TODO: 在此添加命令处理程序代码
MessageBox("View clicked");
}
h运行程序,点击菜单TEST,出现View clicked;删除代码void CMenuView::OnTest(),再点击TEST,出现Doc clicked;然后依次是MainFrame clicked、App clicked。(Shift+ctrl+x打开类向导,删除)
原因(菜单命令路由过程):点击菜单项时,最新收到消息的是CMainFrame框架类,然后它将命令消息交给子窗口CMenuView,如果CMenuView没有响应消息的函数,它就会把消息交给CMenuDoc,CMenuDoc也响应不了消息的话,就会把消息交还CMenuView,CMenuView再把消息交还给CMainFrame,CMainFrame查看自身有没有对该菜单命令的命令响应函数,没有的话,就交给CMenuApp(应用程序类)。
在CMainFrame的OnCreate函数中增添代码。一个窗口创建(Create)之后,会向操作系统发送WM_CREATE消息,OnCreate()函数主要是用来响应此消息的。
//我使用VS2010并不能在OnCreate函数中对菜单进行操作,运行后会崩溃。资料:http://blog.csdn.net/grasshopperwarbler/article/details/6337754visual studio2010里默认采用的是Menu格式在OnCreate()末端并未生成。vs2010默认采用的是增加了扩展功能的Menu,所以调用GetMenu()会得到空指针。
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
//内部代码省略。
//我们增添的代码放在最后。
GetMenu()->GetSubMenu(0)->CheckMenuItem(0,MF_BYPOSITION|MF_CHECKED); //GetSubMenu获取子菜单;CheckMenuItem为菜单项打上勾号标记。
//CheckMenuItem第一个参数取值的含义,由第二个参数决定。当第二个参数取数值为MF_BYCOMMAND的时候,就标识第一个参
数取菜单的ID号;取值为MF_BYPOSITION的时候,第一个参数就要取菜单的索引号。
//所以上面这段代码也可以用下面这段代码表示。
// GetMenu()->GetSubMenu(0)->CheckMenuItem(菜单项ID,MF_BYCOMMAND|MF_CHECKED);
GetMenu()->GetSubMenu(0)->SetDefaultItem(1,TRUE); //缺省菜单项SetDefaultItem使变粗体,一个子菜单中缺省菜单项只能有一个?
//另外一种表达:GetMenu()->GetSubMenu(0)->SetDefaultItem(菜单项ID,FALSE);
//类似的,EnableMenuItem使菜单项变灰、不可使用。注:需要在构造函数中写入m_bAutoMenuEnable=FALSE(资料:如果设置m_bAutoMenuEnable该标志为FALSE的话,EnableMenuItem()就可以正常使用了。没有设置该标志呢?猜测framework会自动更新菜单:根据消息映射检查菜单ID是否有handler,如果有,就将其激活,否则就禁用.所以每次手动设置之后就被自动改回来了...)但,太扯淡了,这时本来该是灰色菜单项都变黑色了(MFC菜单项初始设置可能无效了)。。。
}
下面是VS2010的实现方法(适用于VC++6.0):
添加在CMainFrame::OnCreate的代码:
m_wndMenuBar.ShowWindow(SW_HIDE); //隐藏菜单
CMenu menu; //创建菜单对象
menu.LoadMenuA(IDR_MAINFRAME); //加载菜单ID
SetMenu(&menu); //让菜单显示
menu.Detach(); //让菜单一直有效,不因身处作用域之外而失效。CMENU类的成员函数,切断一个CWnd对象和一个有效窗口的联系。
处理菜单项。以“将剪切菜单项由灰色变可使用状态”为例:
类向导--(Menu,CMainFrame,ID_EDIT_CUT,UPDATE_COMMAND_UI)--添加处理--编辑
void CMainFrame::OnUpdateEditCut(CCmdUI *pCmdUI)
{
// TODO: 在此添加命令更新用户界面处理程序代码
if(ID_EDIT_CUT==pCmdUI->m_nID) //m_nID保留了当前消息存的ID号,m_nIndex保留索引号
pCmdUI->Enable(); //参数为TRUE(可以省略)或者FALSE。pCmdUI->SetCheck(true);把菜单选中
//处理菜单对应的用户界面:每当选单被拉,并尚未显示之前,其命令项(以及对应之工具栏按钮)都会收到UPDATE_COMMAND_UI消息。http://zhidao.baidu.com/question/325558105.html
//这时发现工具栏上的“剪切”图标也变黑色状态了。打开资源--ToorBar--找到图标,发现其ID号也是ID_EDIT_CUT,是可以关联的
}
静态添加右键点击弹出菜单。首先创建一个菜单资源:
view类中添加WM_RBUTTONDOWN消息,代码:
void CMenuView::OnRButtonDown(UINT nFlags, CPoint point)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
CMenu menu;
menu.LoadMenuA(IDR_MENU1);
CMenu *pPopup=menu.GetSubMenu(0);
ClientToScreen(&point); //将坐标转换为相对客户区的坐标。不然是相对屏幕的
pPopup->TrackPopupMenu(TPM_LEFTALIGN|TPM_RIGHTBUTTON,point.x,point.y,this); //在指定位置显示快捷菜单,并跟踪菜单项的选择。参数(快捷菜单的左边界与由参数X指定的坐标对齐|用户能用鼠标左、右键选择菜单项,坐标X,Y,view窗口句柄)
CView::OnRButtonDown(nFlags, point);
}
热键定义:我们可以看下“文件”菜单项的属性,Caption为"文件(&F)”,很明显,只要在要定义为热键的字母前加&就可以了。
快捷键如义:我们再来看看“打开”菜单项的Caption属性,为“打开(&O)...\tCtrl+O”,这里的\t表示在显示前面的文本后跳格再显示快捷键Ctrl+O,但这样设置其Caption属性只是能显示出快捷键,要实现快捷键的功能还需要在Accelerator资源中设定。
Accelerator中有四列,分别为:ID、Modifier、Key和Type。ID就是菜单项的ID,Modifer和Key就代表了组合键。例如,打开菜单项的ID为ID_FILE_OPEN,Modifer为“Ctrl”,Key为“O”。
下面说菜单的动态处理,涉及到GetMenu(),又只有VC++6.0可以运行。但如果在VS2010把有关m_wndMenuBar的代码全都注释了,那就可以正常使用GetMenu()了。(测试完成后,要记得把afxpopupmenu.h有关m_wndMenuBar的代码取消注释,不然会影响其他项目的)
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
//内部代码省略。
//我们增添的代码放在最后。
CMenu menu;
menu.CreatePopupMenu();
GetMenu()->AppendMenuA(MF_POPUP,(UINT)menu.m_hMenu,"创建菜单");
//GetMenu()->InsertMenuA(2,MF_BYPOSITION|MF_POPUP,(UINT)menu.m_hMenu,"插入菜单");
menu.AppendMenuA(MF_STRING,111,"添加菜单项菜单项1");
//动态添加菜单项响应函数
//1:在Resource.h添加 #define IDR_HELLO 111
//2:在MainFrm.h public处添加 afx_msg void OnHello();
//3:在MainFrm.cpp 添加
//void CMainFrame::OnHello()
//{
// MessageBox("Hello");
//}
GetMenu()->DeleteMenu(1,MF_BYPOSITION); //删除菜单
menu.Detach();
return 0;
}