本专栏上几篇文章讲解了MFC几大机制,今天带领大家学习MFC自定义消息以及常用控件,最常用的控件请查看本专栏第一二篇文章,今天这篇文章介绍工具栏,菜单和状态栏,以及菜单创建大总结。
文章目录
- MFC消息分类:
- 菜单创建方法总结
- 1.对话框上直接添加菜单资源:
- 2. 在WM_CREATE消息处理中加载菜单,设置菜单
- 3.框架类Create方法中创建
- 4.使用CMenu对象创建
- MFC范围宏:
- 工具栏控件
- 状态栏控件
- 右键消息处理:
- 总结
MFC消息分类:
-
Windows下的常用消息(标准消息)
win32 WM_CREATE WM_PAINT
-
在Win32消息前添加ON_
WM_COMMAND 菜单按钮,加速键是单独处理的
-
用户自定义消息:
#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()
接下来,我们来看看官方定义:
#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)) },
那么我们就可以自己定义函数:
- 首先,设置签名:
afx_msg LRESULT onMyMsg(WPARAM,LPARAM);
- 实现:
LRESULT CMenuToolBarDlg::onMyMsg(WPARAM W,LPARAM L)
{
AfxMessageBox(L"My_Msg");
return 0;
}
这里就弹一个消息框
3. 我们发送消息看看:
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消息处理中加载菜单,设置菜单
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对象创建
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;
这样时候,我们函数这样写:
int CMenuToolBarDlg::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CDialogEx::OnCreate(lpCreateStruct) == -1)
return -1;
// TODO: 在此添加您专用的创建代码
menu->LoadMenuW(IDR_MENU2);
this->SetMenu(menu);
return 0;
}
这样创建就没有问题了。
MFC范围宏:
我们在处理按钮或者菜单消息等的时候,我们有时候有这种需求:连续几个按钮,都有着相同的回调方法,这时候,我们就可以使用范围宏:
- 首先我们来创建这样一个菜单:
我们将这三个菜单ID设置为连续,我这里是32273,32274,32275
我们到消息映射中使用范围宏来处理:
我们首先来看一下范围宏的定义:
#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)) },
我们到消息映射中添加:
ON_COMMAND_RANGE(ID_32773,ID_32775,onRangle)
然后我们定义回调函数:
void CMenuToolBarDlg::onRangle(UINT id)
{
CString str;
str.Format(L"按钮id = %d", id);
AfxMessageBox(str);
}
这样我们就可以统一处理这三个按钮了:
工具栏控件
- 我们到资源中创建工具栏对象:
- 然后,我们加载工具栏:
首先我们要在类中声明CToolBar成员:
CToolBar toolBar
然后,到Create函数中加载工具栏:
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之前的信息会出现在状态栏上,之后的信息,我们鼠标移动到上面的时候,会直接显示.
状态栏控件
状态栏控件我们不需要添加资源,直接在创建窗口的时候加载就可以了:
类中添加成员
SCtatusBar statusBar;
onCreate消息中:
statusBar.CreateEx(this);
我们发现状态栏只有一项,如果我们想添加,就要定义一个全局数组:
UNIT g_arr[]= {0,ID_TIME,2,};
我们再来设置:
指示器
statusBar.SetIndicators(g_arr,3);
//ID---表示字符串
statusBar.SetPaneInfo(1,ID_TIME,SBPS_NORMAL,100);
这时候就发现可以分项了。
我们来处理一下状态栏:
我们设置系统时间到状态栏上:
消息映射:
ON_WM_TIMER()
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的另一种方式:
消息映射:
N_WM_CONTEXTMENU()
处理:
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);;
}
我们通常使用右键来弹出右键菜单,这里介绍一种方式,弹出右键菜单的时候,可以初始化:
void onInitMenuPopup(CMenu* pMenu,UINT,BOOL){
::CheckMenuItem(pMenu->m_hMenu,ID_DEL,MF_CHECKED);
}
总结
最后总结一下容易出现的误区:
- 我们使用MFC消息映射的时候,很多时候都需要我们自己写函数,但是很多时候我们会写错,不知道参数该些什么,返回值该写什么,实际上,我们查看宏定义(就是消息映射上的宏)就可以看到函数名称,返回值,参数等
- 或者我们再定义宏的时候,可以在括号里写上消息ID和回调函数,就可以解决上述问题
好了,今天的分享就到这里,大家如果发现有什么错误,还请及时指出来,我们大家共同进步!!!