在我们开发桌面软件的时候,我们经常会遇到这样一个场景:我们同一个软件,要给不用的用户群体使用,功能上有些许差别,比如说Visual Studio就有免费社区版,专业版和企业版。
面对不同的用户,我们想要提供的服务是不同的,免费版的软件只开放部分功能,登录的用户是学生或者是教师,提供的服务也是不同的,这时,我们就希望用户登录之后,更具用户类型来动态加载菜单,仅展示他们需要的功能。
那我们应该怎么实现呢?
1、创建Doc模板时传入公共菜单
首先我们来看一下$ProjectName.cpp文件:
BOOL CMFCTestApp::InitInstance()
{
CSingleDocTemplate* pDocTemplate;
pDocTemplate = new CSingleDocTemplate(
IDR_MAINFRAME,
RUNTIME_CLASS(CMFCTestDoc),
RUNTIME_CLASS(CMainFrame), // 主 SDI 框架窗口
RUNTIME_CLASS(CMFCTestView));
if (!pDocTemplate)
return FALSE;
AddDocTemplate(pDocTemplate);
// 分析标准 shell 命令、DDE、打开文件操作的命令行
CCommandLineInfo cmdInfo;
ParseCommandLine(cmdInfo);
// 调度在命令行中指定的命令。 如果
// 用 /RegServer、/Register、/Unregserver 或 /Unregister 启动应用程序,则返回 FALSE。
if (!ProcessShellCommand(cmdInfo))
return FALSE;
// 唯一的一个窗口已初始化,因此显示它并对其进行更新
m_pMainWnd->ShowWindow(SW_SHOW);
m_pMainWnd->UpdateWindow();
return TRUE;
}
我省略掉了前面的OLE初始化等部分,主要来看一下我们关心的部分。
CSingleDocTemplate是Doc模板,构造函数的参数分别是资源ID(可以是菜单,工具栏,状态栏等),文档类,框架类,视类。
ProcessShellCommand(cmdInfo):这句代码根据上面的模板,先后创建了文档类,框架类和视类。然后就是显示窗口。
在创建Doc模板时,我们可以在资源视图中创建一个公共的菜单传入,提供注册,登录等功能,在登录之后提供新的菜单。
2、在视类初始化的时候,更新菜单
在资源文件中为新建的菜单添加资源ID
Resource.h
#define ID_MENU_TEST0 400
#define ID_MENU_TEST1 401
#define ID_MENU_TEST2 402
在初始化视类的时候,我们可以创建一个全新的菜单,如下:
void CMFCTestView::OnInitialUpdate()
{
CMenu myMenu;
myMenu.CreateMenu(); // 创建普通菜单
myMenu.AppendMenu(MF_STRING, ID_MENU_TEST0, _T(“Test0”)); // 在菜单后追加菜单项
myMenu.AppendMenu(MF_STRING, ID_MENU_TEST2, _T(“Test2”));
myMenu.InsertMenu(1, MF_BYPOSITION | MF_STRING, ID_MENU_TEST1, _T(“Test1”)); // 在指定位置插入菜单项,MF_BYPOSITION不能少
AfxGetMainWnd()->SetMenu(&myMenu); // 设置当前菜单
m_hMenuDefault = m_NewMenu.m_hMenu; // 设置默认菜单那
myMenu.Detach();
AfxGetMainWnd()->DrawMenuBar(); // 重绘菜单
}
AppendMenu的第一个参数有以下值(具体请看MSDN文档):
我们也可以在原来菜单的基础上加以修改,增加我们想要的功能,删除不需要的。实现如下:
void CMFCTestView::OnInitialUpdate()
{
CView::OnInitialUpdate();
CMenu *mainMenu = AfxGetMainWnd()->GetMenu(); // 获取当前菜单
CMenu *drawingMenu = NULL;
int menuCount = mainMenu->GetMenuItemCount(); // 获取菜单项数
for (int i = 0; i < menuCount; i++)
{
CString menuName;
if (mainMenu->GetMenuString(i, menuName, MF_BYPOSITION)
&& menuName == "文件(&F)") // 找到名为"文件(&F)"的菜单项
{
drawingMenu = mainMenu->GetSubMenu(i);
break;
}
}
CMenu myMenu;
myMenu.CreatePopupMenu(); //创建弹出菜单,点击弹出子菜单
myMenu.AppendMenu(MF_STRING, ID_MENU_TEST0, _T("Test0"));
myMenu.AppendMenu(MF_STRING, ID_MENU_TEST2, _T("Test2"));
myMenu.InsertMenu(1, MF_BYPOSITION | MF_STRING, ID_MENU_TEST1, _T("Test1"));
// 将新建的菜单添加到文件菜单最后,添加子菜单不需要ID,但是要传入子菜单的句柄
drawingMenu->AppendMenu(MF_POPUP | MF_STRING, (UINT_PTR)myMenu.m_hMenu, _T("Test"));
myMenu.Detach();
AfxGetMainWnd()->DrawMenuBar();
}
3、添加菜单的处理函数
添加完菜单后,如果没有添加命令处理函数的话,我们的菜单会是灰色的,无法选中,所以接下来我们要实现菜单的处理函数,实现如下:
// xxx.h
// 在头文件中声明响应函数
protected:
afx_msg void OnMenuTest();
DECLARE_MESSAGE_MAP()
// xxx.cpp
// 在cpp文件中实现函数,并绑定相关联的菜单ID
void CMFCTestView::OnMenuTest()
{
AfxMessageBox(_T("MenuTest!"));
// TODO:添加命令处理代码
}
BEGIN_MESSAGE_MAP(CMFCTestView, CView)
ON_COMMAND(ID_MENU_TEST0, OnMenuTest) // 绑定ID和处理函数,注意,这里不要分号
END_MESSAGE_MAP()
这样,我们就可以修改菜单,添加我们需要的功能了。
本文仅针对单文档MFC应用程序,后续会再写一篇关于多文档的动态菜单实现的文章,敬请期待。
如果觉得对您有帮助的话,请扫一下这个二维码,和小小僧一起成长吧!