一、菜单的分类
菜单属于框架。
从资源视图进入下图
?弹式菜单:点击后弹出菜单项。其ID无法编辑,Popup属性为True。
?非弹式菜单:无法弹出菜单选项。其ID可编辑,?Popup属性为False
二、菜单响应命令路由
新建一弹式菜单,并为其设置一非弹式菜单Demo
为Demo添加一事件处理程序
依次为应用、框架、文档、视图添加处理程序。
并分别为其添加一个消息框(由于一些类没有MessageBox,因而用全局的)?
void CMFC程序App::OnTestDemo()
{
// TODO: 在此添加命令处理程序代码
AfxMessageBox(TEXT("APP"));
}
void CMainFrame::OnTestDemo()
{
// TODO: 在此添加命令处理程序代码
AfxMessageBox(TEXT("Frame"));
}
void CMFC程序Doc::OnTestDemo()
{
// TODO: 在此添加命令处理程序代码
AfxMessageBox(TEXT("DOC"));
}
void CMFC程序View::OnTestDemo()
{
// TODO: 在此添加命令处理程序代码
MessageBox(TEXT("View"));
}
?而后验证,
?直接点击弹出view。
?注释view部分相关处理程序后,再次点击。
同理,注释文档部分,点击。
在注释框架部分,点击。
然而实际并不是如此。
实际路由情况:?
frame首先接收到,但是不处理->view处理了则中断。未处理->doc处理了则中断。未处理->view
->frame处理了则中断。未处理->app处理(不处理则该按钮为灰色,无法点击)
三、消息分类
命令消息:WM_COMMAND,只有菜单中才有。菜单处理函数选中
标准消息:WM_XXXX,属性->消息。CWnd子类才能接收到标准消息。
通告消息:点击控件,执行处理函数。
CWnd:可以接受任何消息。
CCmdTarget:不能接收标准消息。
四、菜单的静态操作
菜单栏的静态操作可分为:禁用(灰色)、选中(打勾)、默认项(加粗)。
为了方便,本次让框架类处理事件,并在其OnCreate方法中修改让其启动就触发。
选中
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CFrameWnd::OnCreate(lpCreateStruct) == -1)
return -1;
if (!m_wndToolBar.CreateEx(this, TBSTYLE_FLAT, WS_CHILD | WS_VISIBLE | CBRS_TOP | CBRS_GRIPPER | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC) ||
!m_wndToolBar.LoadToolBar(IDR_MAINFRAME))
{
TRACE0("未能创建工具栏\n");
return -1; // 未能创建
}
if (!m_wndStatusBar.Create(this))
{
TRACE0("未能创建状态栏\n");
return -1; // 未能创建
}
m_wndStatusBar.SetIndicators(indicators, sizeof(indicators)/sizeof(UINT));
// TODO: 如果不需要可停靠工具栏,则删除这三行
m_wndToolBar.EnableDocking(CBRS_ALIGN_ANY);
EnableDocking(CBRS_ALIGN_ANY);
DockControlBar(&m_wndToolBar);
//获取菜单
CMenu* menu = GetMenu();
//获取子菜单
CMenu* fileMenu = menu->GetSubMenu(0);//GetMenu属于CWnd,从0开始指代每一个菜单子菜单。此处获取第一栏
//标志“新建”
//参数,怎么确定的参数(位置、ID等)|标志该项目
//此处通过位置确定
fileMenu->CheckMenuItem(0, MF_BYPOSITION | MF_CHECKED);
//标志“打开”
//参数,怎么确定的参数(位置、ID)|标志该项目
//此处通过ID确定
fileMenu->CheckMenuItem(ID_FILE_OPEN, MF_BYCOMMAND | MF_CHECKED);
return 0;
}
默认项
一个菜单项只有一个默认项
//设置默认项,一个菜单项只有一个默认项
//默认项“保存”
//参数,怎么确定的参数(TRUE:位置,FALSE:ID)
//此处通过位置确定
fileMenu->SetDefaultItem(2, TRUE);
禁用
MF_DISABLED:菜单项无效,但不变灰。
MF_GRAYED:菜单项无效,但变灰。
新版本中,MF_DISABLED与?MF_GRAYED无区别,都会变灰。
首先需要将CFrameWnd::m_bAutoMenuEnable设置FALSE,避免它内部自动更新。与构造函数总设置。
CMainFrame::CMainFrame() noexcept
{
// TODO: 在此添加成员初始化代码
m_bAutoMenuEnable = FALSE;
}
//设置禁用项
//首先需要将CFrameWnd::m_bAutoMenuEnable设置false
//禁用“另存为”
//参数,怎么确定的参数(位置、ID)|禁用该项目
fileMenu->EnableMenuItem(3, MF_BYPOSITION|MF_DISABLED);
//禁用“打印”,分隔符要占位
fileMenu->EnableMenuItem(5, MF_BYPOSITION | MF_GRAYED);
五、菜单项的替换
于OnCreate中,通过SetMenu(NULL)即可移除菜单栏。
现在对MFC.rc中Menu文件夹插入新的Menu。
为其设置新的窗口内容,记住其ID。
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CFrameWnd::OnCreate(lpCreateStruct) == -1)
return -1;
if (!m_wndToolBar.CreateEx(this, TBSTYLE_FLAT, WS_CHILD | WS_VISIBLE | CBRS_TOP | CBRS_GRIPPER | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC) ||
!m_wndToolBar.LoadToolBar(IDR_MAINFRAME))
{
TRACE0("未能创建工具栏\n");
return -1; // 未能创建
}
if (!m_wndStatusBar.Create(this))
{
TRACE0("未能创建状态栏\n");
return -1; // 未能创建
}
m_wndStatusBar.SetIndicators(indicators, sizeof(indicators)/sizeof(UINT));
// TODO: 如果不需要可停靠工具栏,则删除这三行
m_wndToolBar.EnableDocking(CBRS_ALIGN_ANY);
EnableDocking(CBRS_ALIGN_ANY);
DockControlBar(&m_wndToolBar);
//移除菜单项目
SetMenu(NULL);
CMenu menu2;
//加载菜单
menu2.LoadMenuW(IDR_MENU1);//输入菜单ID
SetMenu(&menu2);
//Detach():将对象从Windows菜单CMenu中分离,并返回句柄。
//使得该对象不会随着该函数结束后一同被消除
menu2.Detach();
return 0;
}
此处注意,分离后的对象不再是menu2,只是一串存放着有效数据的地址(但仍被程序所知晓,能够通过GetMenu()重新获取)。menu2中原本存放该对象处已为空,因而在其被销毁时不会连累到对象。
执行Detach()前?:
执行Detach()后:
结果如图:
六、菜单的更新
通过下面方式,给a与b添加处理程序(类型不同)。
为Frame添加变量
记得初始化:
CMainFrame::CMainFrame() noexcept
{
// TODO: 在此添加成员初始化代码
m_bAutoMenuEnable = FALSE;
m_isUpdate = false;
}
而后输入代码为:?
void CMainFrame::OnUpdateTestA(CCmdUI* pCmdUI)
{
// TODO: 在此添加命令更新用户界面处理程序代码
if (true == m_isUpdate) {
pCmdUI->Enable(TRUE);
}
else {
pCmdUI->Enable(FALSE);
}
}
void CMainFrame::OnTestB()
{
// TODO: 在此添加命令处理程序代码
m_isUpdate = !m_isUpdate;
}
七、右键点击唤出菜单
为view准备事件鼠标右键按下。
void CMFC程序View::OnRButtonDown(UINT nFlags, CPoint point)//鼠标标志以及点击的坐标
{ //由于菜单是属于框架的,因而要单独为view创建一个
CMenu menu;
//载入需要的菜单
menu.LoadMenuW(IDR_MENU1);
CMenu* subMenu = menu.GetSubMenu(0);
//将客户区域坐标转换为屏幕坐标(左上角原点)。因为弹窗坐标是以屏幕为单位
ClientToScreen(&point);
//标志位(设置鼠标在弹出菜单的位置,通过屏幕位置标志与鼠标键标志组合),
//坐标x,坐标 y,属于哪一个窗口,对齐方式(TRUE,则菜单按从右到左的阅读顺序右对齐;FALSE,则菜单为从左到右的阅读顺序左对齐)
//此处为鼠标在左侧,右键点击,去点击坐标,当前窗口,默认值FALSE
subMenu->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, point.x, point.y, this);
CView::OnRButtonDown(nFlags, point);
}
结果
八、动态图标
如图添加资源,导入ICON
笔者用的是256*256的ico图片。
由于菜单,标题属于框架,因此,为框架准备存储ICON的变量。
?
完成构造函数中的初始化。
通过AfxGetApp获得程序实例(因为构造函数初始化时,还没有创建窗口)
CMainFrame::CMainFrame() noexcept
{
//AfxGetApp获得应用程序实例
icon[0] = AfxGetApp()->LoadIconW(IDI_ICON1);
icon[1] = AfxGetApp()->LoadIconW(IDI_ICON2);
}
?随后为其添加定时器。并在create事件中为其开始计时。
void CMainFrame::OnTimer(UINT_PTR nIDEvent)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
//窗口句柄,修改什么,用什么修改
//当前窗口的句柄,修改图标,图片
//m_hWnd(自带)指明与这个CWnd对象相关联的句柄
static int i = 0;
i = i % 2;
SetClassLong(m_hWnd, GCL_HICON,(LONG)icon[i]);
i++;
CFrameWnd::OnTimer(nIDEvent);
}
结果:
?
|