MFC程序设计(六)消息和控件
消息的分类
在上一节消息映射中,我们发现,无论是create消息还是paint消息使用的都是同一个宏ON_MESSAGE。通过ON_MESSAGE宏我们可以在上一节的静态数组中添加任何消息处理函数,因此ON_MESSAGE也被称为通用宏。但在实际的应用中,在添加消息处理函数时一般不使用通用宏,而是根据添加的消息处理函数的类型而选用不同的宏
在MFC中添加消息使用的宏分为以下三种:
1.标准window消息:ON_WM_XXX
2. 命令消息:WM_COMMAND
3. 用户自定义消息:ON_MESSAGE
标准window消息
标准window消息:ON_WM_XXX,除自定 义的消息和WM_COMMAND消息以外的消息。该消息对应的宏有:ON_WM_CREATE,ON_WM_PAINT,ON_WM_CREATE。
在消息映射添加消息处理函数时宏不需要写参数,每种宏对应默认的消息处理函数名,根据该消息处理函数名进行声明与定义(通过MSDN查找)。
默认的消息处理函数的参数都是经过winSDK过滤优化以后得来的
注意:定义函数结尾返回了父类的消息处理函数,这是因为在窗口创建时除了我们定义的消息处理函数要执行以外,还有其他事情要执行,为保证程序正常运行,把这些事情交给父类对应的消息处理函数处理。
子类消息处理函数返回父类消息处理函数要根据除了子类定义的消息处理函数以外,是否有其他事情要处理而决定是否返回。如OnPaint消息,完成子类消息处理函数后,父类并不用处理其他事情,因此也不会返回父类消息处理函数
命令消息
WM_COMMAND 菜单按钮 加速键
后文细说
用户自定义消息
自定义的消息,操作系统默认没有这种消息类型,因此也没有对应的消息ID,也不能自行捕捉该消息,所以操作系统只能通过人工发送消息来捕捉,然后进行消息处理函数的响应。
接下来我们利用上文中提及的标准window消息OnCreate,来进行发送消息
#define WM_MYMESSAGE MY_USER + 1001//定制消息ID,此时只是为了下文使用时方便好看
BEGIN_MESSAGE_MAP(CMyFrameWdn,CFreamWnd)//消息映射
ON_MESSAGE(WM_MYMESSAGE, OnMyMessage)
ON_WM_CREATE()
END_MESSAGE_MAP(
)
LRESULT OnMyMessage(WPARAM wParam, LPARAM lParam);//类中声明。自定义的函数参数没法过滤
int OnCreate(LPCREATESTRUCT pcs);
int OnCreate(LPCREATESTRUCT pcs)
{
AfxMessageBox("WM_CREATE消息被处理")l
::PostMessageA(thix->m_hWnd, WM_MYMESSAGE, 1, 2);
return CFrameWnd::OnCreate(pcs);
}
LRESULT OnMyMessage(WPARAM wParam, LPARAM lParam)
{
CString str;
str.Format("wParam = %d, lParam = %d", wParam, lParam);
AfxMessageBox(str);
return 0;
}
MFC菜单
菜单简介
菜单在Win32中通过菜单句柄HMENU进行标识,由于MFC是Win32通过C++封装而成的,采用了面向对象的思想,因此MFC将菜单句柄和菜单类对象绑定一对一的关系,实现通过CMenu类对象表示菜单
菜单的使用
1.通过在资源视图中添加资源来添加菜单
2.代码设置菜单到窗口
方法一
#include<afxwin.h>
#include"resource.h"
class CMyFramneWnd : public CFrameWnd {};
class CMyWinApp : public CWinApp
{
public:
virtual BOOL InitInstance();
};
BOOL CMyWinApp::InitInstance()
{
CMyFramneWnd* pFrame = new CMyFramneWnd;
pFrame->Create(NULL, "MFCCreate", WS_OVERLAPPEDWINDOW, CFrameWnd::rectDefault, NULL, (CHAR*)IDR_MENU1);
m_pMainWnd = pFrame;
pFrame->ShowWindow(SW_SHOW);
pFrame->UpdateWindow();
return TRUE;
}
CMyWinApp theApp;
运行结果
当我们没有将子菜单定义消息处理函数时,系统默认灰色不可用
方法二
#include<afxwin.h>
#include"resource.h"
class CMyFrameWnd : public CFrameWnd
{
DECLARE_MESSAGE_MAP()
public:
afx_msg int OnCreate(LPCREATESTRUCT pcs);//afx_msg表示消息处理函数
public:
CMenu menu;//菜单的声明周期同框架类一致
};
BEGIN_MESSAGE_MAP(CMyFrameWnd, CFrameWnd)
ON_WM_CREATE()
END_MESSAGE_MAP()
class CMyWinApp : public CWinApp
{
public:
virtual BOOL InitInstance();
};
BOOL CMyWinApp::InitInstance()
{
CMyFrameWnd* pFrame = new CMyFrameWnd;
pFrame->Create(NULL, "MFCCreate");
m_pMainWnd = pFrame;
pFrame->ShowWindow(SW_SHOW);
pFrame->UpdateWindow();
return TRUE;
}
CMyWinApp theApp;
int CMyFrameWnd::OnCreate(LPCREATESTRUCT pcs)
{
menu.LoadMenu(IDR_MENU1);//1.利用win32的LoadMenu获取菜单句柄 2.句柄和menu对象绑定
this->SetMenu(&menu);//面向对象思想
//也可::SetMenu(this->m_hWnd, menu.m_hMENU),Win32思想
return CFrameWnd::OnCreate(pcs);
}
菜单消息处理
现定义一个消息处理函数,当点击新建菜单项时,出现弹窗
#include<afxwin.h>
#include"resource.h"
class CMyFrameWnd : public CFrameWnd
{
DECLARE_MESSAGE_MAP()
public:
afx_msg int OnCreate(LPCREATESTRUCT pcs);
afx_msg void OnNew();
public:
CMenu menu;//菜单的声明周期同框架类一致
};
BEGIN_MESSAGE_MAP(CMyFrameWnd, CFrameWnd)
ON_WM_CREATE()
ON_COMMAND(ID_NEW, OnNew)
END_MESSAGE_MAP()
class CMyWinApp : public CWinApp
{
public:
virtual BOOL InitInstance();
};
BOOL CMyWinApp::InitInstance()
{
CMyFrameWnd* pFrame = new CMyFrameWnd;
pFrame->Create(NULL, "MFCCreate");
m_pMainWnd = pFrame;
pFrame->ShowWindow(SW_SHOW);
pFrame->UpdateWindow();
return TRUE;
}
CMyWinApp theApp;
int CMyFrameWnd::OnCreate(LPCREATESTRUCT pcs)
{
menu.LoadMenu(IDR_MENU1);
this->SetMenu(&menu);
return CFrameWnd::OnCreate(pcs);
}
void CMyFrameWnd::OnNew()
{
AfxMessageBox("新建菜单项被点击")
}
命令消息处理顺序
应用程序类也可以添加消息映射宏,但实现内部只可以实现菜单消息处理函数
现在我们在应用程序类和框架类的消息映射宏中都添加菜单处理函数:
#include<afxwin.h>
#include"resource.h"
class CMyFrameWnd : public CFrameWnd
{
DECLARE_MESSAGE_MAP()
public:
afx_msg int OnCreate(LPCREATESTRUCT pcs);
afx_msg void OnNew();//afx_msg表示消息处理函数
public:
CMenu menu;//菜单的声明周期同框架类一致
};
BEGIN_MESSAGE_MAP(CMyFrameWnd, CFrameWnd)
ON_WM_CREATE()
ON_COMMAND(ID_NEW, OnNew)
END_MESSAGE_MAP()
class CMyWinApp : public CWinApp
{
DECLARE_MESSAGE_MAP()
public:
virtual BOOL InitInstance();
afx_msg void OnNew();
};
BEGIN_MESSAGE_MAP(CMyWinApp, CWinApp)
ON_COMMAND(ID_NEW, OnNew)
END_MESSAGE_MAP()
CMyWinApp theApp;
BOOL CMyWinApp::InitInstance()
{
CMyFrameWnd* pFrame = new CMyFrameWnd;
pFrame->Create(NULL, "MFCCreate");
m_pMainWnd = pFrame;
pFrame->ShowWindow(SW_SHOW);
pFrame->UpdateWindow();
return TRUE;
}
int CMyFrameWnd::OnCreate(LPCREATESTRUCT pcs)
{
menu.LoadMenu(IDR_MENU1);
this->SetMenu(&menu);
return CFrameWnd::OnCreate(pcs);
}
void CMyWinApp::OnNew()
{
AfxMessageBox("应用程序类处理了新建菜单项被点击");
}
void CMyFrameWnd::OnNew()
{
AfxMessageBox("框架类处理了新建菜单项被点击");
}
运行程序发现,点击新建菜单项时,执行了框架类的消息处理函数
但当框架类消息映射中没有菜单消息处理函数时才会调用应用程序类消息映射中的菜单消息处理函数
因此得出命令消息处理顺序:框架里->应用程序类
设置菜单项状态
#include<afxwin.h>
#include"resource.h"
class CMyFrameWnd : public CFrameWnd
{
DECLARE_MESSAGE_MAP()
public:
afx_msg int OnCreate(LPCREATESTRUCT pcs);
afx_msg void OnNew();//afx_msg表示消息处理函数
afx_msg void OnInitMenuPopup(CMenu* pPopup, UINT nPos, BOOL i);
public:
CMenu menu;//菜单的声明周期同框架类一致
};
BEGIN_MESSAGE_MAP(CMyFrameWnd, CFrameWnd)
ON_WM_CREATE()
ON_COMMAND(ID_NEW, OnNew)
ON_WM_INITMENUPOPUP()
END_MESSAGE_MAP()
class CMyWinApp : public CWinApp
{
public:
virtual BOOL InitInstance();
afx_msg void OnNew();
};
CMyWinApp theApp;
BOOL CMyWinApp::InitInstance()
{
CMyFrameWnd* pFrame = new CMyFrameWnd;
pFrame->Create(NULL, "MFCCreate");
m_pMainWnd = pFrame;
pFrame->ShowWindow(SW_SHOW);
pFrame->UpdateWindow();
return TRUE;
}
int CMyFrameWnd::OnCreate(LPCREATESTRUCT pcs)
{
menu.LoadMenu(IDR_MENU1);
this->SetMenu(&menu);
return CFrameWnd::OnCreate(pcs);
}
void CMyFrameWnd::OnNew()
{
AfxMessageBox("框架类处理了新建菜单项被点击");
}
void CMyFrameWnd::OnInitMenuPopup(CMenu* pPopup, UINT nPos, BOOL i)
{
//pPopup->CheckMenuItem(ID_NEW, MF_CHECKED);
::CheckMenuItem(pPopup->m_hMenu, ID_NEW, MF_CHECKED);//设置菜单项状态
}
右键菜单
菜单分为顶层菜单和弹出式菜单,右键菜单也叫做弹出式菜单
如图所示:文件所在的菜单叫做顶层菜单,新建所在的菜单叫做弹出式菜单,两者组合就是一个完整的大菜单。
我们在上文创建的菜单对象是整个大菜单,绑定的也是整个大菜单
#include<afxwin.h>
#include"resource.h"
class CMyFrameWnd : public CFrameWnd
{
DECLARE_MESSAGE_MAP()
public:
afx_msg int OnCreate(LPCREATESTRUCT pcs);
afx_msg void OnContextMenu(CWnd* pWnd , CPoint pt);
public:
CMenu menu;//菜单的声明周期同框架类一致
};
BEGIN_MESSAGE_MAP(CMyFrameWnd, CFrameWnd)
ON_WM_CREATE()
ON_WM_CONTEXTMENU()
END_MESSAGE_MAP()
class CMyWinApp : public CWinApp
{
public:
virtual BOOL InitInstance();
};
CMyWinApp theApp;
BOOL CMyWinApp::InitInstance()
{
CMyFrameWnd* pFrame = new CMyFrameWnd;
pFrame->Create(NULL, "MFCCreate");
m_pMainWnd = pFrame;
pFrame->ShowWindow(SW_SHOW);
pFrame->UpdateWindow();
return TRUE;
}
int CMyFrameWnd::OnCreate(LPCREATESTRUCT pcs)
{
menu.LoadMenu(IDR_MENU1);
this->SetMenu(&menu);
return CFrameWnd::OnCreate(pcs);
}
void CMyFrameWnd::OnContextMenu(CWnd* pWnd, CPoint pt)
{
HMENU hPopup = ::GetSubMenu(menu.m_hMenu, 0);//第二个参数表示顶层菜单的索引号
::TrackPopupMenu(hPopup, TPM_LEFTALIGN | TPM_TOPALIGN, pt.x, pt.y, 0, this->m_hWnd, NULL);//显示顶层菜单某个菜单项的下拉菜单
//CMenu* pPopup = menu.GetSubMenu(0);
//pPopup->TrackPopupMenu(TPM_LEFTALIGN | TPM_TOPALIGN, pt.x, pt.y, this);//类函数调用
}
此时运行程序后,点击右键出现右键菜单
MFC工具栏
工具栏实际就是一个子窗口,使用工具栏需要包含<afxext.h>头文件
以vs2022为例:
箭头所指的这些叫做工具,它们处于一个栏框中,叫做工具栏。工具栏具有容器作用,其中容纳着工具按钮。工具栏常与菜单绑定使用,工具栏中的工具,菜单中也通常有。
这是工具栏中的某工具,功能是保存源文件
这是菜单项中的某工具,功能也是保存源文件
工具栏中的工具和菜单项中的工具点击后发送的消息相同,因此两者触发的功能也一样。但工具栏和菜单之间并没有什么关系
相关类
CToolBarCtrl类对象表示工具栏中的按钮
CToolBar类对象表示整个工具栏
框架窗口是一个窗口,工具栏也是一个窗口,框架窗口是工具栏的父窗口,两者之间存在停靠关系
工具栏的使用
#include<afxwin.h>
#include"resource.h"
class CMyFramneWnd : public CFrameWnd
{
DECLARE_MESSAGE_MAP();
afx_msg void OnNew();
};
BEGIN_MESSAGE_MAP(CMyFramneWnd, CFrameWnd)
ON_COMMAND(ID_NEW, OnNew)
END_MESSAGE_MAP();
class CMyWinApp : public CWinApp
{
public:
virtual BOOL InitInstance();
};
BOOL CMyWinApp::InitInstance()
{
CMyFramneWnd* pFrame = new CMyFramneWnd;
pFrame->Create(NULL, "MFCToolBar", WS_OVERLAPPEDWINDOW, CFrameWnd::rectDefault, NULL, (CHAR*)IDR_MENU1);
m_pMainWnd = pFrame;
pFrame->ShowWindow(SW_SHOW);
pFrame->UpdateWindow();
return TRUE;
}
CMyWinApp theApp;
void CMyFramneWnd::OnNew()
{
AfxMessageBox("新建菜单项被点击");
}
项目初始化
添加工具栏
添加工具栏
添加按钮一
此时按钮ID和菜单项ID一致,点击按钮实际也就点击菜单项,两者发送的消息是一样的,这也就是所谓的绑定在一起了
添加按钮二
创建一个与菜单项无关的按钮,这时按钮和菜单项ID不同,两者没有关系
添加按钮三
这就是我们所做的工具栏了
注意:当需要删除按钮时,点住按钮往外拽就删除了,常规的删除键只是删除按钮的颜色等等外观设置
创建工具栏
工具栏的创建在窗口创建的函数中创建
#include<afxwin.h>
#include"resource.h"
#include<afxext.h>
class CMyFramneWnd : public CFrameWnd
{
DECLARE_MESSAGE_MAP();
afx_msg void OnNew();
afx_msg void OnSet();
afx_msg int OnCreate(LPCREATESTRUCT pcs);
public:
CToolBar toolbar;
};
BEGIN_MESSAGE_MAP(CMyFramneWnd, CFrameWnd)
ON_COMMAND(ID_NEW, OnNew)
ON_COMMAND(ID_SET, OnSet)
ON_WM_CREATE()
END_MESSAGE_MAP();
class CMyWinApp : public CWinApp
{
public:
virtual BOOL InitInstance();
};
BOOL CMyWinApp::InitInstance()
{
CMyFramneWnd* pFrame = new CMyFramneWnd;
pFrame->Create(NULL, "MFCToolBar", WS_OVERLAPPEDWINDOW, CFrameWnd::rectDefault, NULL, (CHAR*)IDR_MENU1);
m_pMainWnd = pFrame;
pFrame->ShowWindow(SW_SHOW);
pFrame->UpdateWindow();
return TRUE;
}
CMyWinApp theApp;
void CMyFramneWnd::OnNew()
{
AfxMessageBox("新建菜单项被点击");
}
void CMyFramneWnd::OnSet()
{
AfxMessageBox("绿色工具按钮被点击");
}
int CMyFramneWnd::OnCreate(LPCREATESTRUCT pcs)
{
toolbar.CreateEx(this, TBSTYLE_FLAT, WS_CHILD | WS_VISIBLE | CBRS_ALIGN_TOP);
toolbar.LoadToolBar(IDR_TOOLBAR2);
return 0;
}
运行程序,发现窗口出现工具栏
工具栏停靠
实现工具栏可拖拽到别的位置
#include<afxwin.h>
#include"resource.h"
#include<afxext.h>
class CMyFramneWnd : public CFrameWnd
{
DECLARE_MESSAGE_MAP();
afx_msg void OnNew();
afx_msg void OnSet();
afx_msg int OnCreate(LPCREATESTRUCT pcs);
public:
CToolBar toolbar;
};
BEGIN_MESSAGE_MAP(CMyFramneWnd, CFrameWnd)
ON_COMMAND(ID_NEW, OnNew)
ON_COMMAND(ID_SET, OnSet)
ON_WM_CREATE()
END_MESSAGE_MAP();
class CMyWinApp : public CWinApp
{
public:
virtual BOOL InitInstance();
};
BOOL CMyWinApp::InitInstance()
{
CMyFramneWnd* pFrame = new CMyFramneWnd;
pFrame->Create(NULL, "MFCToolBar", WS_OVERLAPPEDWINDOW, CFrameWnd::rectDefault, NULL, (CHAR*)IDR_MENU1);
m_pMainWnd = pFrame;
pFrame->ShowWindow(SW_SHOW);
pFrame->UpdateWindow();
return TRUE;
}
CMyWinApp theApp;
void CMyFramneWnd::OnNew()
{
AfxMessageBox("新建菜单项被点击");
}
void CMyFramneWnd::OnSet()
{
AfxMessageBox("绿色工具按钮被点击");
}
int CMyFramneWnd::OnCreate(LPCREATESTRUCT pcs)
{
toolbar.CreateEx(this, TBSTYLE_FLAT, WS_CHILD | WS_VISIBLE | CBRS_ALIGN_TOP | CBRS_GRIPPER);//CBRS_GRIPPER该风格可设置工具栏拖拽
toolbar.LoadToolBar(IDR_TOOLBAR2);
toolbar.EnableDocking(CBRS_ALIGN_ANY);//工具栏允许停靠处
this->EnableDocking(CBRS_ALIGN_ANY);//窗口允许停靠处
this->DockControlBar(&toolbar, AFX_IDW_DOCKBAR_BOTTOM);//设置工具栏初始停靠位置
return 0;
}