VC动态生成菜单菜单响应及加速键的使用
一、使用环境
本文讲解的使用环境为MFC 的Visual Studio项目的单文档应用程序类型,字符集使用多字节字符集,对话框和多文档应用程序类型稍有不同这里不再讲解说明。
二、读取XML树形结构菜单
本文的上一节已经详细讲解了使用pugixml读取XML树形结构菜单的内容,这里不再重复直接使用。
(1)在CMainFrame类的头文件MainFrm.h中添加树形结构菜单存储结构
public:
//可点击菜单ID 名称 命令 是否使用加速键
typedef struct _CMDINFO
{
UINT nID; //ID
CString strName; //名称
CString strCmd; //命令
bool bAccelkey; //是否使用加速键
_CMDINFO()
{
nID = 0;
strName = "";
strCmd = "";
bAccelkey = false;
}
}CMDINFO, *LPCMDINFO;
private:
//菜单树结构头指针
TS_PMENUNODE m_ptrMenuNode;
//可点击菜单信息
vector<CMDINFO> m_vCmdInfo;
//菜单ID累加存储临时变量
int m_nMenuID;
public:
//菜单ID结束数值
static int m_nMenuIDEnd;
(2)在MainFrm.cpp源文件顶部添加起始菜单ID常量定义和声明菜单ID结束数值定义
#define m_nMenuIDStart 1000
int CMainFrame::m_nMenuIDEnd = m_nMenuIDStart;
(3)在CMainFrame类的构造函数CMainFrame()中添加读取XML树形结构菜单代码,我们的菜单配置XML为menu.xml放置到程序exe所在目录下:
m_ptrMenuNode = new TS_MENUNODE();
char szFullPath[256];
ZeroMemory(szFullPath, 256);
::GetModuleFileName(NULL, szFullPath, 256);
(_tcsrchr(szFullPath, _T('\')))[1] = 0;//删除文件名,只获得路径 字串
CString strPath = szFullPath;
strPath = "menu.xml";
CKernelXml KernelXml;
int num = KernelXml.GetMenuXml(strPath, m_ptrMenuNode);
m_nMenuIDEnd = m_nMenuIDStart;
m_nMenuIDEnd = (num - 1);
三、动态创建菜单
(1)在CMainFrame类的LoadFrame函数最后添加菜单创建代码:
///动态生成菜单//
CMenu *pMenu = CMenu::FromHandle(m_wndMenuBar.GetDefaultMenu());
//清空原有的菜单栏
int num = pMenu->GetMenuItemCount();
for (int i=num-1;i>=0;i--)
{
pMenu->DeleteMenu(i, MF_BYPOSITION);
}
//动态添加菜单
m_nMenuID = m_nMenuIDStart;
vector<CMenu *> pHMenu;
//迭代添加菜单
CreateMenuChildrenNode(pMenu, m_ptrMenuNode, pHMenu);
m_wndMenuBar.CreateFromMenu(pMenu->GetSafeHmenu(), TRUE, TRUE);
int count = pHMenu.size();
for (int i=0;i<count;i )
{
if (NULL!=pHMenu[i])
{
delete pHMenu[i];
pHMenu[i] = NULL;
}
}
pHMenu.clear();
return TRUE;
(2)上面代码中用到的CreateMenuChildrenNode函数,请在CmainFrame类的头文件中声明,并在CmainFrame类的源文件中定义:
private:
bool CreateMenuChildrenNode(CMenu *pMenu, TS_PMENUNODE &pMenuNode, vector<CMenu *> &pHMenu);
bool CMainFrame::CreateMenuChildrenNode(CMenu *pMenu, TS_PMENUNODE &pMenuNode, vector<CMenu *> &pHMenu)
{
bool ret = false;
int count = pMenuNode->ptrChildren.size();
for (int i = 0; i < count; i )
{
CString strType = pMenuNode->ptrChildren[i]->strType.MakeUpper();
if ("POPUP" == strType)
{
//创建并添加
CMenu * hMenu = new CMenu();
hMenu->CreateMenu();
pMenu->AppendMenu(MF_POPUP, (UINT)(hMenu->m_hMenu), pMenuNode->ptrChildren[i]->strName);
pHMenu.push_back(hMenu);
CreateMenuChildrenNode(hMenu, pMenuNode->ptrChildren[i], pHMenu);
}
else if ("SEPARATOR" == strType)
{
//添加
pMenu->AppendMenu(MF_SEPARATOR);
}
else if ("STRING" == strType)
{
UINT nKey = MF_STRING;
if ("true" == pMenuNode->ptrChildren[i]->strChecked.Trim().MakeLower())
{
nKey = nKey | MF_CHECKED;
}
pMenu->AppendMenu(nKey, m_nMenuID, TEXT(pMenuNode->ptrChildren[i]->strName));
//菜单信息
CMDINFO cmdInfo;
cmdInfo.nID = m_nMenuID;
cmdInfo.strName = pMenuNode->ptrChildren[i]->strName;
cmdInfo.strCmd = pMenuNode->ptrChildren[i]->strCmd;
m_vCmdInfo.push_back(cmdInfo);
m_nMenuID ;
}
}
return true;
}
(3)至此我们的菜单及创建完毕
四、添加菜单响应
(1)在CMainFrame类的头文件 DECLARE_MESSAGE_MAP()宏上面添加菜单响应函数声明:
afx_msg void OnMenuItem(UINT id);
afx_msg void OnUpdateItem(CCmdUI *pCmdUI);
(2)在CMainFrame类的源文件的宏映射BEGIN_MESSAGE_MAP(CMainFrame, CFrameWndEx)和END_MESSAGE_MAP()中间添加菜单响应宏映射:
ON_COMMAND_RANGE(m_nMenuIDStart, CMainFrame::m_nMenuIDEnd, &CMainFrame::OnMenuItem)
ON_UPDATE_COMMAND_UI_RANGE(m_nMenuIDStart, CMainFrame::m_nMenuIDEnd, &CMainFrame::OnUpdateItem)
(3)在CMainFrame类的源文件中添加菜单响应函数定义:
void CMainFrame::OnUpdateItem(CCmdUI *pCmdUI)
{
int count = m_vCmdInfo.size();
for (int i = 0; i < count; i )
{
if (pCmdUI->m_nID == m_vCmdInfo[i].nID)
{
//对应的CCmdUI的处理
if ("WJ_NEW_XM" == m_vCmdInfo[i].strCmd)
{
}
else if ("WJ_NEW_WZ" == m_vCmdInfo[i].strCmd)
{
}
//......................................
break;
}
}
}
void CMainFrame::OnMenuItem(UINT id)
{
int count = m_vCmdInfo.size();
for (int i = 0; i < count; i )
{
if (id == m_vCmdInfo[i].nID)
{
//对应的菜单响应函数调用
if ("WJ_NEW_XM"==m_vCmdInfo[i].strCmd)
{
}
else if ("WJ_NEW_WZ" == m_vCmdInfo[i].strCmd)
{
}
//......................................
MessageBox(m_vCmdInfo[i].strName ":" m_vCmdInfo[i].strCmd);
break;
}
}
}
(4)至此菜单响应添加完毕:
五、加速键的添加
(1)在CMainFrame类的头文件 DECLARE_MESSAGE_MAP()宏上面添加加速键响应函数声明:
afx_msg LRESULT OnHotKey(WPARAM wParam, LPARAM lParam);
(2)在CMainFrame类的源文件的宏映射BEGIN_MESSAGE_MAP(CMainFrame, CFrameWndEx)和END_MESSAGE_MAP()中间添加加速键宏映射:
ON_MESSAGE(WM_HOTKEY, OnHotKey)
(3)在CMainFrame类的源文件中添加加速键响应函数定义:
LRESULT CMainFrame::OnHotKey(WPARAM wParam, LPARAM lParam)
{
int count = m_vCmdInfo.size();
for (int i = 0; i < count; i )
{
if (wParam == m_vCmdInfo[i].nID)
{
//对应的菜单响应函数的加速键函数调用
if ("WJ_NEW_XM" == m_vCmdInfo[i].strCmd)
{
}
else if ("WJ_NEW_WZ" == m_vCmdInfo[i].strCmd)
{
}
//......................................
MessageBox(m_vCmdInfo[i].strName " 加速键响应:" m_vCmdInfo[i].strCmd);
break;
}
}
return 0;
}
(4)在CreateMenuChildrenNode函数的cmdInfo.strCmd = pMenuNode->ptrChildren[i]->strCmd;和m_vCmdInfo.push_back(cmdInfo);代码之间添加加速键的注册代码:
//判断是否有加速键
if (""!=pMenuNode->ptrChildren[i]->strAccelkey)
{
//注册加速键
vector <CString> vecString;
SplitCString(pMenuNode->ptrChildren[i]->strAccelkey,"|", vecString);
//MOD_ALT MOD_SHIFT MOD_WIN MOD_CONTROL MOD_NOREPEAT
int num = vecString.size();
nKey = 0;
for (int m = 0; m < num; m )
{
CString UpString = vecString[m].Trim().MakeUpper();
if ("MOD_ALT" == UpString)
{
nKey = nKey | MOD_ALT;
}
else if ("MOD_SHIFT" == UpString)
{
nKey = nKey | MOD_SHIFT;
}
else if ("MOD_WIN" == UpString)
{
nKey = nKey | MOD_WIN;
}
else if ("MOD_CONTROL" == UpString)
{
nKey = nKey | MOD_CONTROL;
}
else if ("MOD_NOREPEAT" == UpString)
{
nKey = nKey | MOD_NOREPEAT;
}
}
UINT vk = 0;
if (1 == vecString[num - 1].Trim().GetLength())
{
//字母键时直接转义
vk = vecString[num - 1][0];
}
else
{
vk = GetCodeByName((LPTSTR)(LPCTSTR)vecString[num - 1].Trim());
}
RegisterHotKey(m_hWnd, m_nMenuID, nKey, vk);
cmdInfo.bAccelkey = true;
}
(5)字符串拆分函数SplitCString和由名称得到键值的转义函数GetCodeByName的实现:
1、在CmainFrame类的头文件中声明,并在CmainFrame类的源文件中定义:
int SplitCString(CString strSource, CString strCh, vector <CString> &vecString);
UINT GetCodeByName(LPTSTR pszMsg);
int CMainFrame::SplitCString(CString strSource, CString strCh, vector <CString> &vecString)
{
int iPos = 0;
CString strTmp;
strTmp = strSource.Tokenize(strCh, iPos);
while (strTmp.Trim() != _T(""))
{
vecString.push_back(strTmp);
strTmp = strSource.Tokenize(strCh, iPos);
}
return 0;
}
UINT CMainFrame::GetCodeByName(LPTSTR pszMsg)
{
for (UINT i = 0; i < sizeof(msgInfo) / sizeof(CMainFrame::MSGINFO); i )
{
if (lstrcmp(msgInfo[i].psz, pszMsg) == 0)
{
return msgInfo[i].msg;
}
}
return 0;
}
2、键值转义结构MSGINFO的声明和定义
2.1、在CMianFrame类的头文件CMDINFO结构下方定义MSGINFO结构
typedef struct _MSGINFO {
UINT msg; //键值宏定义
LPTSTR psz; //键名
} MSGINFO, *LPMSGINFO;
2.2、在CMianFrame类的源文件上方位置定义msgInfo
CMainFrame::MSGINFO msgInfo[] = {
{ VK_LBUTTON,TEXT("VK_LBUTTON") },
{ VK_RBUTTON,TEXT("VK_RBUTTON") },
{ VK_CANCEL, TEXT("VK_CANCEL") },
{ VK_MBUTTON,TEXT("VK_MBUTTON") },
{ VK_XBUTTON1, TEXT("VK_XBUTTON1") },
{ VK_XBUTTON2, TEXT("VK_XBUTTON2") },
{ VK_BACK,TEXT("VK_BACK") },
{ VK_TAB, TEXT("VK_TAB") },
{ VK_CLEAR,TEXT("VK_CLEAR") },
{ VK_RETURN,TEXT("VK_RETURN") },
{ VK_SHIFT,TEXT("VK_SHIFT") },
{ VK_CONTROL,TEXT("VK_CONTROL") },
{ VK_MENU,TEXT("VK_MENU") },
{ VK_PAUSE,TEXT("VK_PAUSE") },
{ VK_CAPITAL,TEXT("VK_CAPITAL") },
{ VK_KANA,TEXT("VK_KANA") },
{ VK_HANGEUL,TEXT("VK_HANGEUL") },
{ VK_HANGUL,TEXT("VK_HANGUL") },
{ VK_JUNJA,TEXT("VK_JUNJA") },
{ VK_FINAL,TEXT("VK_FINAL") },
{ VK_HANJA,TEXT("VK_HANJA") },
{ VK_KANJI, TEXT("VK_KANJI") },
{ VK_ESCAPE,TEXT("VK_ESCAPE") },
{ VK_CONVERT, TEXT("VK_CONVERT") },
{ VK_NONCONVERT, TEXT("VK_NONCONVERT") },
{ VK_ACCEPT,TEXT("VK_ACCEPT") },
{ VK_MODECHANGE, TEXT("VK_MODECHANGE") },
{ VK_SPACE,TEXT("VK_SPACE") },
{ VK_PRIOR, TEXT("VK_PRIOR") },
{ VK_NEXT,TEXT("VK_NEXT") },
{ VK_END,TEXT("VK_END") },
{ VK_HOME, TEXT("VK_HOME") },
{ VK_LEFT,TEXT("VK_LEFT") },
{ VK_UP, TEXT("VK_UP") },
{ VK_RIGHT,TEXT("VK_RIGHT") },
{ VK_DOWN,TEXT("VK_DOWN") },
{ VK_SELECT,TEXT("VK_SELECT") },
{ VK_PRINT, TEXT("VK_PRINT") },
{ VK_EXECUTE, TEXT("VK_EXECUTE") },
{ VK_SNAPSHOT, TEXT("VK_SNAPSHOT") },
{ VK_INSERT, TEXT("VK_INSERT") },
{ VK_DELETE,TEXT("VK_DELETE") },
{ VK_HELP,TEXT("VK_HELP") },
{ VK_LWIN,TEXT("VK_LWIN") },
{ VK_RWIN,TEXT("VK_RWIN") },
{ VK_APPS,TEXT("VK_APPS") },
{ VK_SLEEP,TEXT("VK_SLEEP") },
{ VK_NUMPAD0,TEXT("VK_NUMPAD0") },
{ VK_NUMPAD1,TEXT("VK_NUMPAD1") },
{ VK_NUMPAD2,TEXT("VK_NUMPAD2") },
{ VK_NUMPAD3,TEXT("VK_NUMPAD3") },
{ VK_NUMPAD4,TEXT("VK_NUMPAD4") },
{ VK_NUMPAD5,TEXT("VK_NUMPAD5") },
{ VK_NUMPAD6, TEXT("VK_NUMPAD6") },
{ VK_NUMPAD7,TEXT("VK_NUMPAD7") },
{ VK_NUMPAD8,TEXT("VK_NUMPAD8") },
{ VK_NUMPAD9, TEXT("VK_NUMPAD9") },
{ VK_MULTIPLY,TEXT("VK_MULTIPLY") },
{ VK_ADD,TEXT("VK_ADD") },
{ VK_SEPARATOR,TEXT("VK_SEPARATOR") },
{ VK_SUBTRACT,TEXT("VK_SUBTRACT") },
{ VK_DECIMAL,TEXT("VK_DECIMAL") },
{ VK_DIVIDE,TEXT("VK_DIVIDE") },
{ VK_F1,TEXT("VK_F1") },
{ VK_F2,TEXT("VK_F2") },
{ VK_F3,TEXT("VK_F3") },
{ VK_F4,TEXT("VK_F4") },
{ VK_F5,TEXT("VK_F5") },
{ VK_F6,TEXT("VK_F6") },
{ VK_F7,TEXT("VK_F7") },
{ VK_F8,TEXT("VK_F8") },
{ VK_F9,TEXT("VK_F9") },
{ VK_F10,TEXT("VK_F10") },
{ VK_F11,TEXT("VK_F11") },
{ VK_F12,TEXT("VK_F12") },
{ VK_F13,TEXT("VK_F13") },
{ VK_F14,TEXT("VK_F14") },
{ VK_F15,TEXT("VK_F15") },
{ VK_F16,TEXT("VK_F16") },
{ VK_F17,TEXT("VK_F17") },
{ VK_F18,TEXT("VK_F18") },
{ VK_F19,TEXT("VK_F19") },
{ VK_F20,TEXT("VK_F20") },
{ VK_F21,TEXT("VK_F21") },
{ VK_F22,TEXT("VK_F22") },
{ VK_F23,TEXT("VK_F23") },
{ VK_F24,TEXT("VK_F24") },
{ VK_NUMLOCK,TEXT("VK_NUMLOCK") },
{ VK_SCROLL,TEXT("VK_SCROLL") },
{ VK_OEM_NEC_EQUAL,TEXT("VK_OEM_NEC_EQUAL") },
{ VK_OEM_FJ_JISHO,TEXT("VK_OEM_FJ_JISHO") },
{ VK_OEM_FJ_MASSHOU,TEXT("VK_OEM_FJ_MASSHOU") },
{ VK_OEM_FJ_TOUROKU,TEXT("VK_OEM_FJ_TOUROKU") },
{ VK_OEM_FJ_LOYA,TEXT("VK_OEM_FJ_LOYA") },
{ VK_OEM_FJ_ROYA,TEXT("VK_OEM_FJ_ROYA") },
{ VK_LSHIFT,TEXT("VK_LSHIFT") },
{ VK_RSHIFT,TEXT("VK_RSHIFT") },
{ VK_LCONTROL,TEXT("VK_LCONTROL") },
{ VK_RCONTROL,TEXT("VK_RCONTROL") },
{ VK_LMENU,TEXT("VK_LMENU") },
{ VK_RMENU,TEXT("VK_RMENU") },
{ VK_BROWSER_BACK,TEXT("VK_BROWSER_BACK") },
{ VK_BROWSER_FORWARD,TEXT("VK_BROWSER_FORWARD") },
{ VK_BROWSER_REFRESH,TEXT("VK_BROWSER_REFRESH") },
{ VK_BROWSER_STOP,TEXT("VK_BROWSER_STOP") },
{ VK_BROWSER_SEARCH,TEXT("VK_BROWSER_SEARCH") },
{ VK_BROWSER_FAVORITES,TEXT("VK_BROWSER_FAVORITES") },
{ VK_BROWSER_HOME,TEXT("VK_BROWSER_HOME") },
{ VK_VOLUME_MUTE,TEXT("VK_VOLUME_MUTE") },
{ VK_VOLUME_DOWN,TEXT("VK_VOLUME_DOWN") },
{ VK_VOLUME_UP,TEXT("VK_VOLUME_UP") },
{ VK_MEDIA_NEXT_TRACK,TEXT("VK_MEDIA_NEXT_TRACK") },
{ VK_MEDIA_PREV_TRACK, TEXT("VK_MEDIA_PREV_TRACK") },
{ VK_MEDIA_STOP,TEXT("VK_MEDIA_STOP") },
{ VK_MEDIA_PLAY_PAUSE, TEXT("VK_MEDIA_PLAY_PAUSE") },
{ VK_LAUNCH_MAIL,TEXT("VK_LAUNCH_MAIL") },
{ VK_LAUNCH_MEDIA_SELECT,TEXT("VK_LAUNCH_MEDIA_SELECT") },
{ VK_LAUNCH_APP1,TEXT("VK_LAUNCH_APP1") },
{ VK_LAUNCH_APP2,TEXT("VK_LAUNCH_APP2") },
{ VK_OEM_1,TEXT("VK_OEM_1") },
{ VK_OEM_PLUS,TEXT("VK_OEM_PLUS") },
{ VK_OEM_COMMA,TEXT("VK_OEM_COMMA") },
{ VK_OEM_MINUS,TEXT("VK_OEM_MINUS") },
{ VK_OEM_PERIOD,TEXT("VK_OEM_PERIOD") },
{ VK_OEM_2,TEXT("VK_OEM_2") },
{ VK_OEM_3,TEXT("VK_OEM_3") },
{ VK_OEM_4, TEXT("VK_OEM_4") },
{ VK_OEM_5,TEXT("VK_OEM_5") },
{ VK_OEM_6,TEXT("VK_OEM_6") },
{ VK_OEM_7, TEXT("VK_OEM_7") },
{ VK_OEM_8, TEXT("VK_OEM_8") },
{ VK_OEM_AX,TEXT("VK_OEM_AX") },
{ VK_OEM_102, TEXT("VK_OEM_102") },
{ VK_ICO_HELP,TEXT("VK_ICO_HELP") },
{ VK_ICO_00,TEXT("VK_ICO_00") },
{ VK_PROCESSKEY,TEXT("VK_PROCESSKEY") },
{ VK_ICO_CLEAR,TEXT("VK_ICO_CLEAR") },
{ VK_PACKET,TEXT("VK_PACKET") },
{ VK_OEM_RESET,TEXT("VK_OEM_RESET") },
{ VK_OEM_JUMP,TEXT("VK_OEM_JUMP") },
{ VK_OEM_PA1,TEXT("VK_OEM_PA1") },
{ VK_OEM_PA2,TEXT("VK_OEM_PA2") },
{ VK_OEM_PA3,TEXT("VK_OEM_PA3") },
{ VK_OEM_WSCTRL,TEXT("VK_OEM_WSCTRL") },
{ VK_OEM_CUSEL,TEXT("VK_OEM_CUSEL") },
{ VK_OEM_ATTN,TEXT("VK_OEM_ATTN") },
{ VK_OEM_FINISH,TEXT("VK_OEM_FINISH") },
{ VK_OEM_COPY,TEXT("VK_OEM_COPY") },
{ VK_OEM_AUTO,TEXT("VK_OEM_AUTO") },
{ VK_OEM_ENLW,TEXT("VK_OEM_ENLW") },
{ VK_OEM_BACKTAB,TEXT("VK_OEM_BACKTAB") },
{ VK_ATTN, TEXT("VK_ATTN") },
{ VK_CRSEL,TEXT("VK_CRSEL") },
{ VK_EXSEL,TEXT("VK_EXSEL") },
{ VK_EREOF, TEXT("VK_EREOF") },
{ VK_PLAY, TEXT("VK_PLAY") },
{ VK_ZOOM,TEXT("VK_ZOOM") },
{ VK_NONAME,TEXT("VK_NONAME") },
{ VK_PA1,TEXT("VK_PA1") },
{ VK_OEM_CLEAR,TEXT("VK_OEM_CLEAR") }
};
(6)程序结束取消加速键的注册
用类向导给CMainFrame类添加WM_DESTORY消息的消息响应函数OnDestroy函数,在函数最后添加取消加速键的注册代码:
//取消注册加速键
int count = m_vCmdInfo.size();
for (int i = 0; i < count; i )
{
if (m_vCmdInfo[i].bAccelkey)
{
UnregisterHotKey(m_hWnd, m_vCmdInfo[i].nID);
}
}
(7)至此我们的Visual Studio项目的单文档应用程序的动态生成菜单菜单响应及加速键的使用就全部完成了,谢谢大家的支持: