本节学习了菜单编程方面的内容,包括静态菜单操作和动态菜单操作两大块。
静态菜单操作包括标记菜单,默认菜单,图形菜单的实现原理及具体实现,快捷弹出菜单的实现方式及其命令响应函数的添加。
动态菜单操作主要包括:如何让程序运行时产生新的子菜单和菜单项,以及如何手工地为这些新产生的菜单项命令添加消息响应处理函数。
本节还实现了如何在顶层窗口,即框架类窗口中截获对菜单命令的处理。
通过这节还应该了解Windows中消息的分类,以及菜单命令消息的路由过程,进一步熟悉Cstring类的应用。
静态菜单操作
//1.标记菜单(2种方法)
//GetMenu()->GetSubMenu(0)->CheckMenuItem(0,MF_BYPOSITION| MF_CHECKED); //通过菜单项的索引设置一个标记菜单
//GetMenu()->GetSubMenu(0)->CheckMenuItem(ID_FILE_NEW,MF_BYCOMMAND| MF_CHECKED); //通过菜单项的标识设置一个标记菜单
//2.默认菜单
//GetMenu()->GetSubMenu(0)->SetDefaultItem(1,TRUE); //通过菜单项的索引设置一个默认菜单,最后一个参数为TRUE
//GetMenu()->GetSubMenu(0)->SetDefaultItem(ID_FILE_OPEN,FALSE); //通过菜单项的标识设置一个默认菜单最后一个参数为FALSE
//GetMenu()->GetSubMenu(0)->SetDefaultItem(5,TRUE); //设置打印菜单项时索引时5,不是4,因为分隔栏在子菜单中是占据索引位置的
//一个子菜单只能有一个默认菜单项
//3.图形标记菜单
/*CStringstr;
str.Format("x=%d,y=%d",GetSystemMetrics(SM_CXMENUCHECK),GetSystemMetrics(SM_CYMENUCHECK));
//CString类的Format函数可以按照一定的格式把内容格式化,然后将结果保存到CString类型的字符串对象中
//GetSystemMetrics()函数可以取得窗口上各种资源的尺寸,根据参数决定
//根据取得的尺寸信息可知,位图大小应该是13*13;
MessageBox(str);*/
//m_bitmap.LoadBitmap(IDB_BITMAP1);
//GetMenu()->GetSubMenu(0)->SetMenuItemBitmaps(0,MF_BYPOSITION,&m_bitmap,&m_bitmap);//运行程序发现不显示位图,
//原因在于位图太大,只是显示了位图左上角的一小部分内容。
//4.禁用菜单项
//GetMenu()->GetSubMenu(0)->EnableMenuItem(1,MF_BYPOSITION |MF_DISABLED | MF_GRAYED);
//GetMenu()->GetSubMenu(0)->EnableMenuItem(ID_FILE_OPEN,MF_BYCOMMAND| MF_DISABLED | MF_GRAYED);
//在构造函数中修改成员变量才能使这行代码起作用m_bAutoMenuEnable= FALSE;
//MF_DISABLED| MF_GRAYED这两个标志一起使用
//5.移除和装载菜单
//SetMenu(NULL);//移除
//装载
//CMenumenu;
//menu.LoadMenu(IDR_MAINFRAME);
//SetMenu(&menu);
//menu是个局部变量,在程序运行时,点击“打印预览”,关闭“打印预览”,再关闭程序,程序会报错
//解决办法是把menu定义为CMainFram类的成员变量
//还可以用Detach函数把菜单句柄与这个菜单对象分离
//menu.Detach();
return0;
}
//m_bAutoMenuEnable= FALSE;
//一旦在CMainFrame类的构造函数中把成员变量m_bAutoMenuEnable =FALSE;后,就不需要对ON_UPDATE_COMMAND_UI
//或ON_COMMAND消息进行响应处理了,CMenu类的EnableMenuItem函数将能正常工作。
//但是在Menu程序编辑子菜单下的几个菜单项不在以灰色显示了,因为m_bAutoMenuEnable= FALSE后,
//MFC就不在利用它的菜单命令更新机制去判断哪个菜单可以使用,哪个菜单不能够使用,所以其也就不能根据菜单项的状态以不同的外观
//来显示。而菜单能否使用这些判断操作,就需要我们自己去完成了
//说实话,命令更新机制讲的不清楚啊!不明白啊!
//6.MFC菜单命令更新机制
//在消息映射中添加ON_UPDATE_COMMAND_UI宏来捕获CN_UPDATE_COMMAND_UI消息
void CMainFrame::OnUpdateEditCut(CCmdUI*pCmdUI)
{
//TODO: Add your command update UI handler code here
//pCmdUI->Enable();//在这样的消息中带有一个指向CCmdUI对象的指针,可以用它调用相应的功能函数
//在工具栏中剪切的标识和菜单栏中的剪切标识是一样,所以两者都被启用了。
if(2== pCmdUI->m_nIndex) //判断当前是否是新建菜单项,实际中无需判断,MFC调用菜单命令更新函数时,已经确定了特定的菜单项。
pCmdUI->Enable();
}
void CMainFrame::OnUpdateFileNew(CCmdUI*pCmdUI)
{
//TODO: Add your command update UI handler code here
//if(ID_FILE_NEW== pCmdUI->m_nID) //根据标识判断,工具栏中,菜单栏中的剪切都被禁用
if(0== pCmdUI->m_nIndex) //根据索引判断,工具栏中的剪切不被禁用,两者索引不同!
pCmdUI->Enable(FALSE);
}
//如果要在程序中设置某个菜单项的状态,首先通过ClassWizard为这个菜单项添加ON_UPDATE_COMMAND_UI消息响应函数,然后再这个
//函数中进行状态的设置即可!
void CMainFrame::OnShow()
{
//TODO: Add your command handler code here
MessageBox("Mainshow");
}
//动态菜单操作
//1.添加菜单项目
/*CMenumenu;
menu.CreateMenu();//创建一个菜单,然后将其与一个CMenu对象相关联起来。
GetMenu()->AppendMenu(MF_POPUP,(UINT)menu.m_hMenu,"Test"); //添加菜单项
menu.Detach();//将菜单句柄与菜单对象之间的关联断开*/
//2.插入菜单项目
/*CMenumenu;
menu.CreateMenu();//创建一个菜单,然后将其与一个CMenu对象相关联起来。
GetMenu()->InsertMenu(2,MF_POPUP|MF_BYPOSITION, (UINT)menu.m_hMenu,"Test");
menu.AppendMenu(MF_STRING,IDM_HELLO,"Hello");
menu.AppendMenu(MF_STRING,112,"Bye");
menu.AppendMenu(MF_STRING,113,"Mybole");
menu.Detach();
GetMenu()->GetSubMenu(0)->AppendMenu(MF_STRING,114,"Welcome");
GetMenu()->GetSubMenu(0)->InsertMenu(ID_FILE_OPEN,MF_STRING| MF_BYCOMMAND,115,"VC编程");
//3.删除菜单
GetMenu()->DeleteMenu(1,MF_BYPOSITION);
GetMenu()->GetSubMenu(0)->DeleteMenu(2,MF_BYPOSITION);*/
//4.动态添加的菜单项的命令响应
void CMainFrame::OnHello()
{
MessageBox("Hello");
}
ON_COMMAND(IDM_HELLO,OnHello)
afx_msg void OnHello();
//5.电话本示例程序
void CMenu2View::OnChar(UINT nChar, UINTnRepCnt, UINT nFlags)
{
CClientDCdc(this);
if(0x0d== nChar)
{
if(0== ++m_nIndex)
{
m_menu.CreatePopupMenu();
GetParent()->GetMenu()->AppendMenu(MF_POPUP,(UINT)m_menu.m_hMenu,"phoneBook");//菜单是框架类窗口的,要用GetParent()
//取到框架窗口的指针
GetParent()->DrawMenuBar();//取得框架窗口的指针,然后重绘菜单栏,才会在按下回车键后显示先添加的菜单!
//在CMainFrame中不需要这样做,是因为代码是在OnCreate中添加的,窗口还未创建完成
//而此时窗口已经创建完成
}
m_menu.AppendMenu(MF_STRING,IDM_PHONE1+ m_nIndex,m_strLine.Left(m_strLine.Find(' ')));//取得输入的内容空格以前的内容,添加到菜单项中
m_strArray.Add(m_strLine);//m_strArray是定义的一个CStringArray的集合类
//MFC为我们提供了一些非常有用的集合类,这些集合类类似于数组的功能,但他们可以很方便的动态增加和删除元素。
m_strLine.Empty();//在按下回车键后,清空字符串的内容,再次输入时显示的就是新输入的,不会在先前的后面输出了
Invalidate();//让窗口的整个客户区无效,这样,当下一条WM_PAINT消息发生时,窗口就会被更新。不然再次输入的内容
//会再先前输入的内容上面显示。默认参数是TRUE,在窗口重绘时就会把窗口的背景擦除掉。为FALSE时保留窗口的背景
}
else
{
m_strLine+= nChar;
dc.TextOut(0,0,m_strLine);
}
CView::OnChar(nChar,nRepCnt, nFlags);
}
void CMenu2View::OnPhone1()
{
//TODO: Add your command handler code here
CClientDCdc(this);
dc.TextOut(0,0,m_strArray.GetAt(0));
}
void CMenu2View::OnPhone2()
{
//TODO: Add your command handler code here
CClientDCdc(this);
dc.TextOut(0,0,m_strArray.GetAt(1));
}
void CMenu2View::OnPhone3()
{
//TODO: Add your command handler code here
CClientDCdc(this);
dc.TextOut(0,0,m_strArray.GetAt(2));
}
void CMenu2View::OnPhone4()
{
//TODO: Add your command handler code here
CClientDCdc(this);
dc.TextOut(0,0,m_strArray.GetAt(3));
}
ON_COMMAND(IDM_PHONE1,OnPhone1) //这些代码要放到注释宏外面,如果放在里面,在ClassWizard对话框中,在Message Maps选项卡下
ON_COMMAND(IDM_PHONE2,OnPhone2) //的Member functions列表中就会列出这些函数,放在外面就不会列出来。当ClassWizard发现
ON_COMMAND(IDM_PHONE3,OnPhone3) //菜单项已经被删除了的时候,它就会把已为该菜单项添加的消息映射宏也删除了,这样,消息映射宏
ON_COMMAND(IDM_PHONE4,OnPhone4) //的三个环节就断了一环,程序就会出错。
afx_msgvoid OnPhone1(); //放在两个注释宏之间也没有错
afx_msgvoid OnPhone2(); //但为了区分这是自己手动添加的,可以把它们拿出来放在两个AFX_MSG注释宏之后。
afx_msgvoid OnPhone3();
afx_msgvoid OnPhone4();