【MFC】08.MFC消息,自定义消息,常用控件(MFC菜单创建大总结),工具栏,状态栏-笔记

本专栏上几篇文章讲解了MFC几大机制,今天带领大家学习MFC自定义消息以及常用控件,最常用的控件请查看本专栏第一二篇文章,今天这篇文章介绍工具栏,菜单和状态栏,以及菜单创建大总结。

文章目录

MFC消息分类:

  • Windows下的常用消息(标准消息)

    win32 WM_CREATE WM_PAINT

  • 在Win32消息前添加ON_

    WM_COMMAND 菜单按钮,加速键是单独处理的

  • 用户自定义消息:

    c 复制代码
    #define MY_MSG WM_USER+N
    用户自定义消息,我们在MFC中使用通配:ON_MESSAGE(ID,PFUN)
    
    也就是说:
    在消息映射中,
    BEGIN_MESSAGE_MAP(CMyFrameWdn,CFreamWnd)
      ON_WM_CREATE()
    END_MESSAGE_MAP()
    
    而这个消息,MFC早就帮我们写好了,我们可以自己去看一看,由于这是一个虚函数,我们也可以重写:
    在类中:
    int OnCreate(LPCRATESTRUCT cs){
      AfxMessageBox(L"WM_CREATE");
      return 0;
    }
    我们运行发现,这时候,WM_CREATE消息,处理的时候,就是我们自己写的函数了
  • 用户自定义消息:

    首先,我们需要去定义宏,我们自定义消息:
    #define MY_MSG WM_USER+1

    用户自定义消息,MFC已经帮我们写好了统配消息映射:ON_MESSAGE()

    接下来,我们来看看官方定义:

cpp 复制代码
#define ON_MESSAGE(message, memberFxn) \
	{ message, 0, 0, 0, AfxSig_lwl, \
		(AFX_PMSG)(AFX_PMSGW) \
		(static_cast< LRESULT (AFX_MSG_CALL CWnd::*)(WPARAM, LPARAM) > \
		(memberFxn)) },

那么我们就可以自己定义函数:

  1. 首先,设置签名:
cpp 复制代码
	afx_msg LRESULT onMyMsg(WPARAM,LPARAM);
  1. 实现:
cpp 复制代码
LRESULT CMenuToolBarDlg::onMyMsg(WPARAM W,LPARAM L)
{
	AfxMessageBox(L"My_Msg");
	return 0;
}

这里就弹一个消息框

  1. 我们发送消息看看:
cpp 复制代码
int CMenuToolBarDlg::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
	if (CDialogEx::OnCreate(lpCreateStruct) == -1)
		return -1;

	// TODO:  在此添加您专用的创建代码
	AfxMessageBox(L"onCreate");
	SendMessage(MY_MSG, 0, 0);
	return 0;
}

这里是重写了Create函数,发送了我们自定义的消息

菜单创建方法总结

之前几篇文章中我们已经讲解过了好几种加载菜单的方法,今天我们来总结一下

1.对话框上直接添加菜单资源:

这种方法可谓是非常简单了,直接在对话框属性上添加菜单,MFC会自动帮我们生成代码:

首先我们创建菜单资源:

然后到对话框属性中设置就可以了:

这样就算设置好了,我们运行:

我们发现对话框已经创建好了

2. 在WM_CREATE消息处理中加载菜单,设置菜单

cpp 复制代码
int CMenuToolBarDlg::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
	if (CDialogEx::OnCreate(lpCreateStruct) == -1)
		return -1;

	// TODO:  在此添加您专用的创建代码
	HMENU hMenu = LoadMenu((HINSTANCE)GetWindowLongPtr(m_hWnd, GWLP_HINSTANCE), MAKEINTRESOURCE(IDR_MENU1));
	::SetMenu(m_hWnd, hMenu);
	return 0;
}

这样运行之后,我们发现菜单也创建出来了

3.框架类Create方法中创建

我们前面在介绍MFC几大机制的时候,介绍了框架类,我们使用框架类的Create方法也可以创建菜单:

基本使用方法就是CFRame::Create(...);

4.使用CMenu对象创建

cpp 复制代码
int CMenuToolBarDlg::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
	if (CDialogEx::OnCreate(lpCreateStruct) == -1)
		return -1;

	// TODO:  在此添加您专用的创建代码
	CMenu menu;
	menu.LoadMenuW(IDR_MENU1);
	this->SetMenu(&menu);

	return 0;
}

使用CMenu这种方法也可以创建菜单,但是创建了菜单之后,点击之后,会崩溃:

(这里我创建的MFC是基于对话框的,之前在基于单文档架构的时候会崩溃)

这是应为CMenu对象被析构

解决方法:在类中声明CMenu成员,new出来,这样就不会被析构:

我们首先声明成员:CMenu* menu = new CMenu;

这样时候,我们函数这样写:

cpp 复制代码
int CMenuToolBarDlg::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
	if (CDialogEx::OnCreate(lpCreateStruct) == -1)
		return -1;

	// TODO:  在此添加您专用的创建代码
	menu->LoadMenuW(IDR_MENU2);
	this->SetMenu(menu);

	return 0;
}

这样创建就没有问题了。

MFC范围宏:

我们在处理按钮或者菜单消息等的时候,我们有时候有这种需求:连续几个按钮,都有着相同的回调方法,这时候,我们就可以使用范围宏:

  1. 首先我们来创建这样一个菜单:

    我们将这三个菜单ID设置为连续,我这里是32273,32274,32275
    我们到消息映射中使用范围宏来处理:
    我们首先来看一下范围宏的定义:
cpp 复制代码
#define ON_COMMAND_RANGE(id, idLast, memberFxn) \
	{ WM_COMMAND, CN_COMMAND, (WORD)id, (WORD)idLast, AfxSigCmd_RANGE, \
		(AFX_PMSG) \
		(static_cast< void (AFX_MSG_CALL CCmdTarget::*)(UINT) > \
		(memberFxn)) },

我们到消息映射中添加:

cpp 复制代码
ON_COMMAND_RANGE(ID_32773,ID_32775,onRangle)

然后我们定义回调函数:

cpp 复制代码
void CMenuToolBarDlg::onRangle(UINT id)
{
	CString str;
	str.Format(L"按钮id = %d", id);
	AfxMessageBox(str);
}

这样我们就可以统一处理这三个按钮了:

工具栏控件

  1. 我们到资源中创建工具栏对象:
  2. 然后,我们加载工具栏:
    首先我们要在类中声明CToolBar成员:
    CToolBar toolBar
    然后,到Create函数中加载工具栏:
cpp 复制代码
	if (!toolBar.CreateEx(this, TBSTYLE_FLAT, WS_CHILD | WS_VISIBLE | CBRS_ALIGN_TOP | CBRS_GRIPPER | CBRS_TOOLTIPS) 
		|| !toolBar.LoadToolBar(IDR_TOOLBAR1))
	{ 
		AfxMessageBox(TEXT("Failed to create toolbar!"), NULL, NULL);
		return FALSE;
	}
	
m_toolBar.EnableDocking(CBRS_ALIGN_ANY);

this->EnableDocking(CBRS_ALIGN_ANY);

this->DockControlBar(&m_toolBar,AFX|IDW_DOCKBAR_TOP);

很多软件的提示栏,我们鼠标移动到上面的时候,都有提示信息,我们也可以到工具栏的按钮上面添加提示信息:

比如紫色\n按钮

\n之前的信息会出现在状态栏上,之后的信息,我们鼠标移动到上面的时候,会直接显示.

状态栏控件

状态栏控件我们不需要添加资源,直接在创建窗口的时候加载就可以了:

cpp 复制代码
类中添加成员
SCtatusBar statusBar;

onCreate消息中:
statusBar.CreateEx(this);

我们发现状态栏只有一项,如果我们想添加,就要定义一个全局数组:

UNIT g_arr[]= {0,ID_TIME,2,};

我们再来设置:

cpp 复制代码
指示器 
statusBar.SetIndicators(g_arr,3);
//ID---表示字符串
statusBar.SetPaneInfo(1,ID_TIME,SBPS_NORMAL,100);

这时候就发现可以分项了。

我们来处理一下状态栏:

我们设置系统时间到状态栏上:

消息映射:

cpp 复制代码
ON_WM_TIMER()
cpp 复制代码
void CMenuToolBarDlg::OnTimer(UINT_PTR id)
{
		SYSTEMTIME time;
		::GetLocalTime(&time);
		CString str;
		str.Format(L"%d-%d-%d %d:%d:%d", time.wYear, time.wMonth, time.wDay, time.wHour, time.wMinute, time.wSecond);

		staubar.SetPaneText(1, str);
}

右键消息处理:

以前我们处理右键单击消息都是通过消息来处理的,今天我来带领大家使用MFC的另一种方式:

消息映射:

cpp 复制代码
N_WM_CONTEXTMENU()

处理:

cpp 复制代码
void onContextMenu(CWnd*,CPoint pt){
  CMenu menu;
  menu.LoadMenu(IDR_MENU1);
  CMenu* pPopMenu = menu.GetSubMenu(0);
  ::tRACKpOPUPmENU(PpOPmENU->m_hMenu,TPM_CENTERALIGN,pt.x,pt.y,0,this->hWnd,NULL);;
}

我们通常使用右键来弹出右键菜单,这里介绍一种方式,弹出右键菜单的时候,可以初始化:

cpp 复制代码
void onInitMenuPopup(CMenu* pMenu,UINT,BOOL){
  ::CheckMenuItem(pMenu->m_hMenu,ID_DEL,MF_CHECKED);
}

总结

最后总结一下容易出现的误区:

  1. 我们使用MFC消息映射的时候,很多时候都需要我们自己写函数,但是很多时候我们会写错,不知道参数该些什么,返回值该写什么,实际上,我们查看宏定义(就是消息映射上的宏)就可以看到函数名称,返回值,参数等
  2. 或者我们再定义宏的时候,可以在括号里写上消息ID和回调函数,就可以解决上述问题
    好了,今天的分享就到这里,大家如果发现有什么错误,还请及时指出来,我们大家共同进步!!!
相关推荐
怀澈12219 分钟前
高性能服务器模型之Reactor(单线程版本)
linux·服务器·网络·c++
chnming198742 分钟前
STL关联式容器之set
开发语言·c++
威桑1 小时前
MinGW 与 MSVC 的区别与联系及相关特性分析
c++·mingw·msvc
美式小田1 小时前
单片机学习笔记 9. 8×8LED点阵屏
笔记·单片机·嵌入式硬件·学习
熬夜学编程的小王1 小时前
【C++篇】深度解析 C++ List 容器:底层设计与实现揭秘
开发语言·数据结构·c++·stl·list
yigan_Eins1 小时前
【数论】莫比乌斯函数及其反演
c++·经验分享·算法
Mr.131 小时前
什么是 C++ 中的初始化列表?它的作用是什么?初始化列表和在构造函数体内赋值有什么区别?
开发语言·c++
猫爪笔记1 小时前
前端:HTML (学习笔记)【2】
前端·笔记·学习·html
阿史大杯茶1 小时前
AtCoder Beginner Contest 381(ABCDEF 题)视频讲解
数据结构·c++·算法
_不会dp不改名_1 小时前
HCIA笔记3--TCP-UDP-交换机工作原理
笔记·tcp/ip·udp