基于单文档MFC的菜单的操作

基于单文档的MFC标准的程序

菜单命令的消息路由:

由快到慢
视图 (最快) > 文档 > 框架类 > 应用程序类
如果同一个菜单命令 在上面四个中都有处理函数,则只相应 视图,如果将视图中处理函数删掉,则只相应 文档类的,以此类推。
**-
基于单文档MFC的菜单的操作_第1张图片

关于菜单的几个操作

在MainFrm.cpp的OnCreate函数下:

思路:获取主菜单–》获取子菜单-》给第3和5个菜单项做标记√–》给第5个子菜单 设为默认项–》给第5个变灰

【注意】分隔符也占一个位置啊,打印是第6个,但参数里面是5哦,从零开始数。

//获取主菜单
	CMenu *menu = GetMenu();//获取主菜单,
	
	//获取子菜单
	CMenu *Filemenu = menu->GetSubMenu(0);//获取子菜单

	//标记√  两种方式
	Filemenu->CheckMenuItem(2,MF_BYPOSITION | MF_CHECKED);
	Filemenu->CheckMenuItem(ID_FILE_PRINT_SETUP,MF_BYCOMMAND | MF_CHECKED);

	//设置默认项 一个菜单只有一个默认项   FALSE为 ID ;  true 为位置
	//Filemenu->SetDefaultItem(ID_FILE_PRINT,FALSE);//打印(P)...
	Filemenu->SetDefaultItem(3,true);////打印(P)...注意分隔符也占一个位置啊

	//变灰  CFrameWnd:: m_bAutoMenuEnable = false;
	//注意变灰这个,需要在构造函数中 添加m_bAutoMenuEnable = false;!!!!
	Filemenu->EnableMenuItem(5,MF_BYPOSITION | MF_DISABLED);

	//分隔符设置问题--》》》》》  右击菜单 属性  separator  改为false即可

加载新菜单

**思路:**如果有旧的,可以先移除,加载新菜单,设置菜单

//移除菜单//为NULL时全部移除
SetMenu(NULL);//CMenu::RemoveMenu()必须给指定的删除哪一个子项

//加载菜单
	CMenu menuNew;
	menuNew.LoadMenu(IDR_NEW_MENU);
	SetMenu(&menuNew);
	menuNew.Detach();//这个必须要有,没有程序崩溃

如果只有上面前三行程序则会程序崩溃,因为menuNew是一个局部变量。
解决办法:
一: CMenu menuNew; 设置为全局变量
二: 在上面代码中添加 menuNew.Detach();即可

几个重要函数:

  • SetMenu()

原型BOOL SetMenu(HWND hWnd,HMENU hMenu);
功能:该函数分配一个新菜单到指定窗口
参数:
hWnd:菜单被分配到其中的窗口的句柄。
HMenu:新菜单的句柄。如果菜单参数为NULL,则窗口的当前菜单被删除。 函数SetMenu替换原来的菜单(如果存在),但并不将其销毁。应用程序必须调用函数DestroyMenu来销毁菜单。

  • TrackPopupMenu
    功能:弹出子菜单
    BOOL TrackPopupMenu( UINT nFlags, int x, int y, CWnd* pWnd,LPCRECT lpRect = NULL );
    参数: x,y是指屏幕左边。如果是View中添加WM_LBUTTONDOWM 获得Point是客户区坐标,需要用ClientToScreen(&point)来转换
  • 子菜单栏的有两种消息
    一:ON_UPDATE_COMMAND_UI 处理用户界面显示状态:变灰,√
    二:COMMAND :点击按键,设置变量m_bIsUpdate来控制上面的情况
    消息:ON_UPDATE_COMMAND_UI 选中子菜单-> 右击 添加事件处理程序–选择 CMainFrame。这个消息可以处理当前子菜单的状态,比如何时变灰啊,加√啊,加文字,加单选啊,用其他函数下的变量【标志位】来控制这个何时需要何种变化状态。
    用这种方式比 在OnCreate中( 先获得主菜单,再获得子菜单,在操作子菜单变灰 )这种要方便,因为菜单是内部自动更新,会有重绘等操作,会相应这个消息的。

菜单更新机制

ON_UPDATE_COMMAND_UI 这个消息来,菜单有内部自动更新功能,通过标志位来更新菜单。

子菜单的界面更新

重点】用一个标志变量去控制子菜单的更新,在框架类中实现的


    //ON_UPDATE_COMMAND_UI 消息的作用:
//功能:点击B 使A变灰或者不变灰//菜单栏是自动内部更新的,重新调用绘制菜单栏
//菜单是内部自动更新
//A的子菜单的更新
void CMainFrame::OnUpdateTest(CCmdUI *pCmdUI)
{
	// TODO: 在此添加命令更新用户界面处理程序代码
	if(true == m_bIsUpdata)
	{
		pCmdUI->Enable(TRUE);
//		pCmdUI->SetCheck();
		pCmdUI->SetRadio();
	}
	else
	{		
		pCmdUI->Enable(FALSE);
		pCmdUI->SetCheck(false);
	}
}

//m_bIsUpdata 全局私有变量 
//点击B菜单项时处理函数  命令处理函数
void CMainFrame::OnTestB()//B的消息处理函数
{
	// TODO: 在此添加命令处理程序代码
	m_bIsUpdata = !m_bIsUpdata;//控制A中菜单更新
}

【关于基于对话框的菜单栏没有更新的问题】
参考https://blog.csdn.net/xuanyin235/article/details/80905655

视图中操作菜单:左键单击弹出子菜单项

  • 在客户区点击鼠标左键时,弹出子菜单窗口
    由于客户区的话,则在视图窗口添加WM_LBUTTONDOWM消息。
    思路:
    【报错】获取主菜单-> 获取子菜单->操作就可以
    【正确】加载菜单->获取子菜单-》坐标转换-》弹出子菜单
    但CMenu *menu = GetMenu();//成功
    CMenu *SubMenu = menu->GetSubMenu(0);//不成功
    这个获取就不成功呢,是因为菜单是属于框架类的,如果在视图中操作时,需要先加载菜单项,然后在处理而且,弹出的子菜单项再点击时不会相应像mainfrm.cpp中消息
//在客户区点击鼠标左键时,弹出子菜单项
void CMy05_MenueView::OnLButtonDown(UINT nFlags, CPoint point)
{
	// TODO: 在此添加消息处理程序代码和/或调用默认值
	//菜单是属于框架类的,如果在视图中操作时,需要先加载菜单项,然后在处理。//而且,弹出的子菜单项再点击时不会相应像mainfrm.cpp中消息
	 CMenu mee;
	 mee.LoadMenuW(IDR_NEW_MENU);
	 CMenu *SubMenu = mee.GetSubMenu(0);
	 ClientToScreen(&point);
	SubMenu->TrackPopupMenu(TPM_CENTERALIGN | TPM_LEFTBUTTON,point.x,point.y,this);
	CView::OnLButtonDown(nFlags, point);
}

点击菜单第2个子菜单,使第3个或者其他子菜单项更新状态

加载动态图标

标题是属于框架类的,不属于视图,,框架包含菜单栏,客户区等。视图只要客户区啊。。。

  • 添加资源–导入图标资源
  • 在MainFrame.h中定义图标的句柄,如果多个用数组:HICON hICon[4];
  • 在CMainFrame::CMainFrame()构造函数中 给图标句柄初始化
    hICon[0] = AfxGetApp()->LoadIcon(IDI_ICON1);
	hICon[1] = AfxGetApp()->LoadIcon(IDI_ICON2);
	hICon[2] = AfxGetApp()->LoadIcon(IDI_ICON3);
	hICon[3] = AfxGetApp()->LoadIcon(IDI_ICON4);
  • 在OnCreate中 开启定时器 SetTimer(12,500,NULL);
  • 在定时器中修改标题: SetClassLong函数非常重要
if(12 == nIDEvent)
	{
		static int i = 0;
		SetClassLong(m_hWnd,GCL_HICON,(LONG)hICon[i]);
		i++;
		if(4 == i)
		{
			i = 0;
		}
	}

函数SetClassLong

功能:设置或者修改窗口类的某些参数
原型:
DWORD SetClassLong(HWND hWnd,int nlndex,LONG dwNewLong)

hWnd:窗口句柄及间接给出的窗口所属的类。
nlndex:指定将被替换的32位值
dwNewLong:指定的替换值。
返回值:
返回值的类型:DWORD

如果要设置WNDCLASSEX结构中的任何值,需要指定下面索引之一:

GCL_CBCLSEXTRA:设置与类相关的尺寸的字节大小。设定该值不改变己分配的额外字节数。
GCL_CBWNDEXTRA:设置与类中的每一个窗口相关的尺寸的字节大小。设定该值不改变已分配额外字节数。查看如何进入该内存,参看SetWindowLOng。
GCL_HBRBACKGROUND:替换与类有关的背景刷子的句柄。
GCL_HCURSOR:替换与类有关的光标的句柄。
GCL_HICON:替换与类有关的图标的句柄。
GCL_HMODULE:替换注册类的模块的句柄。
GCL_STYLE:替换窗口类的风格位。
GCL_MENUNAME :替换菜单名字符串的地址。该字符串标识与类有关的菜单资源。
GCL_WNDPROC :替换与窗口类有关的窗口过程的地址。

函数 GetClassLong

功能:获取现有类型
原型DWORD GetClassLong(HWND hWnd,int nlndex);

DWORD SetClassLong 替换指定窗口所属类的WNDCLASSEX结构,对窗口所属类的窗口进行修改,

LONG SetWindowLong 该函数改变指定窗口的属性,对具体的窗口的样式尺寸等进行修改
区别:一个是操作所属类,一个操作具体窗口,而且返回值不太一样

在孙鑫写的那本书上的例子:

SetWindowLong(m_hWnd, GWL_STYLE,   GetWindowLong(m_hWnd,GWL_STYLE));

【备注】单文档程序中
A 视图 CView :: CWnd
B 框架 CFrame::CWnd
C 文档 Cdocument :: CCmdTarget
D 应用程序类 CWinApp:: CCmdTarget
AB可以用CWnd::MessagBox CD只能用AfxMessageBox 因为CD不是继承与CWnd

你可能感兴趣的:(MFC)