//vc 创建动态菜单及其响应函数 vc 创建动态响应的菜单
工作中时常需要变动一些项目,这就可能涉及到动态增加,删除菜单等问题。为了简单起见,使用SDI单文档的MainFrame,主要想借用它的菜单,同时从数组获得动态项目,实际可从数据库,文件等来获得。动态菜单的中心思想是将动态项目ID控制在一定的范围,这样一个响应函数就可分类处理它们。当然相同功能的按钮,只要ID控制在一定范围,本程序稍加改进也可利用。对于在视图,对话框等的情况,只要将其中的获得菜单方法适当改变即可。
直接上代码:
//一 动态菜单的开始和结束ID,为了简单动态从数组获得,实际可从数据库,文件中获得。
#define IDC_CMD_RANGE_1 (WM_USER+100)//连续命令,开始序号
#define IDC_CMD_RANGE_N (WM_USER+200)//连续命令,结束序号
CString strDynMenuName[]={"NotePad.exe","calc.exe",""};//动态项目
//二 动态菜单相关的函数
//为了演示全面,建立了3个类函数,增加,删除,处理动态菜单
// void AddDynamicMenu();
// void DelDynamicMenu(CString strMenuName);
// void ProcessDynamicMenu(UINT nID);
// 2.1 添加动态函数
void CMainFrame::AddDynamicMenu()
{
//1 定义CMenu对象
CMenu popupMenu,menu1,menu2;
CMenu haveMain,*pMainMenu;
//haveMain.LoadMenu(IDR_MENU1); //从资源加载,
//2 创建固定子菜单1
menu1.CreateMenu();
menu1.AppendMenu(0,(IDC_CMD_RANGE_1+11),"http://www.sohu.com"); // 第1项菜单项
menu1.AppendMenu(0,(IDC_CMD_RANGE_1+12),"mailto:524392421@qq.com?subject=demo&Body=test vc Dynamic menu "); // 第2项菜单项
//3 创建动态子菜单2
//这里为了简单,从数组得到。实际可以从数据库,磁盘文件等动态获得数据。
int menuIdx=0;
menu2.CreateMenu();
int num=sizeof(strDynMenuName)/sizeof(CString); //动态数组个数 /sizeof(strDynMenuName[0])
for (menuIdx=0;menuIdx<num;menuIdx++)
{
menu2.AppendMenu(0,IDC_CMD_RANGE_1+21+menuIdx,strDynMenuName[menuIdx]);
}
//4 创建弹出式菜单
popupMenu.CreatePopupMenu();
popupMenu.AppendMenu(MF_BYPOSITION|MF_POPUP|MF_STRING, IDC_CMD_RANGE_1,"删除动态菜单");
popupMenu.AppendMenu(MF_SEPARATOR);
popupMenu.AppendMenu(MF_BYPOSITION|MF_POPUP|MF_STRING, (UINT) menu1.m_hMenu,"固定项目");
popupMenu.AppendMenu(MF_SEPARATOR);
popupMenu.AppendMenu(MF_BYPOSITION|MF_POPUP|MF_STRING, (UINT) menu2.m_hMenu,"动态项目");
//5 动态为主菜单添加项目
//取得指向窗口菜单的Cmenu对象的指针。
pMainMenu=GetMenu(); //AfxGetMainWnd()->GetMenu();
//将弹出式菜单插入到第4项菜单帮助之前(菜单项从0开始计算,文件,编辑,查看,帮助)。
//haveMain.GetSubMenu(0)-> m_hMenu是被装入菜单的第一个菜单项的弹出式菜单的菜单句柄。
CString strMenu="工作动态菜单";
//pMainMenu->InsertMenu (3,MF_POPUP|MF_BYPOSITION|MF_STRING, (UINT)haveMain.GetSubMenu(0)->m_hMenu,strMenu);
pMainMenu->InsertMenu (1,MF_POPUP|MF_BYPOSITION|MF_STRING, (UINT)popupMenu.m_hMenu,strMenu);
//附加到最后,就是帮助之后
//pMainMenu->AppendMenu(MF_BYPOSITION|MF_POPUP|MF_STRING, (UINT) popupMenu.m_hMenu,strMenu);
//6 将窗口菜单与Cmenu对象分离。
pMainMenu->Detach();
//haveMain.Detach();
menu1.Detach();
menu2.Detach();
popupMenu.Detach();
//7 重画菜单。
DrawMenuBar();
}
//2.2 删除动态函数
void CMainFrame::DelDynamicMenu(CString strMenuName)
{
CMenu *pMainMenu;
CString str;
pMainMenu=GetMenu(); //取得指向窗口菜单的Cmenu对象的指针。
for(int i=pMainMenu->GetMenuItemCount()-1;i>=0;i--) //取得菜单的项数。
{
//获得菜单项的标签
pMainMenu->GetMenuString(i,str,MF_BYPOSITION);
if (str.Find("动态菜单")>=0)//(str==strMenuName)//删除动态菜单
{
pMainMenu->DeleteMenu(i,MF_BYPOSITION);
break;
}
}
pMainMenu->Detach(); //将窗口菜单与Cmenu对象分离。
DrawMenuBar(); //重画菜单。
}
//2.3 处理动态菜单工作的函数
void CMainFrame::ProcessDynamicMenu(UINT nEventID)
{
CString strMenuText; //菜单项名
GetMenu()->GetMenuString(nEventID,strMenuText,MF_STRING);//根据ID得到菜单项名
int num=nEventID-IDC_CMD_RANGE_1 ;//实际使用的序号
if (num==0)
{//删除动态菜单
DelDynamicMenu("动态菜单");//strMenuName);
}
if (11<=num && num<=20)
{//固定项目处理,比如访问网站,发Email等
ShellExecute(NULL,NULL,strMenuText,NULL,NULL,SW_SHOWNORMAL);
}
if (21<=num && num<=30)
{//动态部分处理
CString strCmd;
strCmd=strDynMenuName[num-21];
ShellExecute(this->m_hWnd,NULL,strCmd,NULL,NULL,SW_SHOWNORMAL);
}
}
//三 动态加入自己的菜单到系统菜单
// 在函数 OnCreate中,调用加入动态菜单
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
AddDynamicMenu();//OnCreate最后调用加入动态菜单
}
//四 加入动态响应函数,有两种方法,各有利弊
//方法1 ,比较简单仅需加入系统的OnCmdMsg,只是所有命令消息都要进入,消息多,再从中判断命令ID,是否自己的即可。
BOOL CMainFrame::OnCmdMsg(UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO* pHandlerInfo)
{
if (0 == nCode)//判断是否是命令消息
{
if (IDC_CMD_RANGE_1 <= nID && nID<=IDC_CMD_RANGE_N)//在指定范围内
{
if (pHandlerInfo==NULL )
{
ProcessDynamicMenu(nID);
}
return true;
}
}
return CFrameWnd::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo);
}
//方法2 ,稍微复杂,但仅处理自己范围的命令ID,消息少
//2.3 MainFrame.cpp 类中加入自己消息函数实现
void CMainFrame::OnDynamicCommandRange(UINT nEventID)
{
//响应菜单命令系统函数BOOL CMainFrame::OnCommand(WPARAM wParam, LPARAM lParam) 使用2个参数,
//但是,自定义范围的OnCommandRange(WPARAM wParam, LPARAM lParam) ,使用2个参数 Release 就会失败
ProcessDynamicMenu(nEventID);
}
//五 附录
5.1
菜单命令消息处理过程:
当点击一个菜单项时,CMainFrame框架类最先接收,它会把菜单项消息交给它的子窗口View类,由View类首先进行处理;如果View类检测到没对该菜单项消息做响应,则View类把菜单项消息继续交给文档类 Doc类进行处理;如果Doc类检测到Doc类中也没对该菜单项消息做响应,则Doc类又把该菜单项消息返还给View类,由View类再交还给 CMainFrame类处理。如果CMainFrame类查看到CMainFrame类中也没对该消息做响应,则最终交给App类进行处理。而且一个消息一旦在某个类中被响应过,则不再接着传递。
就像上面的OnCmdMsg处理了就返回 return true; 而不再返回 return CFrameWnd::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo);
5.2
添加菜单项消息响应函数:
MFC确定一个Command ID 是否有Handler与之对应,是通过OnCmdMsg(UINT nID, int nCode, void *pExtra, AFX_CMDHANDLERINFO* pHandlerInfo),然后根据返回值来确定的。返回值为true,表示有对应的处理函数;返回值为false,表示没有。其中参数nID就是发送过来的消息ID号,对于菜单,就是菜单的ID;参数nCode为0表示是命令消息,如单击菜单项发出的消息,为-1就表示是 UPDATE_COMMAND_UI消息;参数pHandleInfo不为NULL时,表示在检测;当其为NULL时表示是在路由这个命令消息,希望能得到处理。
5.3
调用的ProcessDynamicMenu(nID);可用PostMessage(WM_USER+300,nID);
然后也可加入消息 WindowProc 进行处理
LRESULT CMainFrame::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{
if (message == WM_USER+300)
{
UINT nID=LOWORD(wParam); //取 低位
if (IDC_CMD_RANGE_1 <= nID && nID<=IDC_CMD_RANGE_N)//在指定范围内
{
ProcessDynMenu(nID);
}
}
return CFrameWnd::WindowProc(message, wParam, lParam);
}