仿MFC消息机制封装对话框窗口类
仿MFC消息机制封装对话框窗口类
这几天,又看了网上不少MFC的学习视频,学习了不少知识,对MFC消息机制有了不少的认识,于是便有了根据MFC消息机制再次封装一次对话框类,
class QDialog:public QWnd
{
//....
}
继承QWnd类,想要用MFC消息机制就不能免要有一个父类,因为在查找消息时,在本类找不到就要去父类查找嘛.
头文件的定义
1.先定义一个函数指针类型
//////////////////////////////////////////////////////////////////////////
// 自定义函数指针类型
typedef LRESULT(QWnd::*AFX_PMSG)(WPARAM, LPARAM);
2.定义一个消息结构体
//////////////////////////////////////////////////////////////////////////
// 自定义消息结构体
struct MSGSTRUCT
{
UINT uMsg; //消息id号
UINT uCtrID; //控件id号
UINT uCod; //控件反射的事件id号
AFX_PMSG pfn; //函数指针
};
大体就定义这么几个,其实MFC中还有一个函数类型afxSing,就是为区分各种不同样式的函数形式,我试了下,要写N多的函数样式,太烦,时间一长谁知道这么多的样式呀.
所以我就只定一个统一的样式.(LRESULT xxx (WPARAM,LPARAM);)到用的时候再函数上注释上参数的各种样式不就好了.
3.定义消息映射宏.
这可费了老鼻子劲了.
都知道,MFC的消息映射分为,
1在类内声明宏,
在类中创建宏,声明三个函数,GetMessageMap(),GetThisMessageMap(),FindMsg()
#define CREATE_MESSAGE_MAP()\
protected:\
const MSGSTRUCT* GetMessageMap()const;\
static const MSGSTRUCT* getThisMessageMap();\
virtual const MSGSTRUCT* FindMsg(UINT uMsg, WPARAM wParam,LPARAM lParam);\
MFC只声明了二个函数,它是用链表来循环查找消息的,我没有哪么弄,太烦嗦了,我用虚函数来查找,
反正要传父类进来的参数,在本类没找到直接返回父类的FindMsg()中去找,父类没找到去它的父类查找,直到找到为止,
比用链表是不是理解上简单多了.(以后有时间再去弄链表)
在类中声明的位置,在哪都可以,一般是放在构造函数之后,我这是测试所以放在开始,我也懒得调了:
//////////////////////////////////////////////////////////////////////////
// QDialog Class
class QDialog :public QWnd
{
CREATE_MESSAGE_MAP() //创建消息映射宏
public:
QDialog(DWORD dlgIDD,QWnd* pParent=nullptr);
QDialog();
BOOL CreatedialogX(HINSTANCE hInstance,DWORD lpDlgIDD, QWnd* pParent = NULL); //创建对话框(以模板资源id创建)
static LRESULT CALLBACK lpDlgProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM); //消息处理
INT_PTR DoModel(QWnd* pParent,LPVOID lpVoidData = 0); //模态对话框
virtual LRESULT OnInitDialog(WPARAM wParam, LPARAM lParam); //初始化对话框消息,我写成虚函数,子类直接重写
public://msg
afx_msg LRESULT OnDestroy(WPARAM wParam, LPARAM lParam); //窗口销毁消息
afx_msg LRESULT OnClose(WPARAM wParam, LPARAM lParam); //窗口关闭消息
afx_msg LRESULT OnIDok(WPARAM wParam, LPARAM lParam); //IDOK按钮消息 };
2在类外实现宏
实现宏放在构造函数之后
QDialog::QDialog()
{ }
//这是各种消息和函数对应的宏的原型,后面是用的消息宏封装了一下,方便添加消息和函数对应时的书写,
//{WM_CLOSE,0,0,(AFX_PMSG)&OnClose},
//{WM_INITDIALOG, 0, 0, (AFX_PMSG)&OnInitDialog},
//{WM_DESTROY,0,0,(AFX_PMSG)&OnDestroy},
BEGIN_MESSAGE_MAP(QDialog,QWnd)
ON_WM_MSG(WM_DESTROY,&QDialog::OnDestroy)
ON_WM_MSG(WM_CLOSE,&QDialog::OnClose)
ON_WM_MSG(WM_INITDIALOG,&QDialog::OnInitDialog)
END_MESSAGE_MAP() BOOL QDialog::CreatedialogX(HINSTANCE hInstance,DWORD lpDlgIDD, QWnd* pParent /*= NULL*/)
{
m_hParent = (pParent != NULL) ? pParent->Gethwnd() : nullptr;
m_hInstance = hInstance;
m_DlgIDD = lpDlgIDD;
m_hWnd = ::CreateDialogParam(hInstance, MAKEINTRESOURCE(lpDlgIDD), m_hParent, (DLGPROC)lpDlgProc, (LPARAM)this);
if (!m_hWnd)
return FALSE;
HICON hIcon = ::LoadIcon(nullptr, IDI_APPLICATION);
SendMessage(m_hWnd, WM_SETICON, ICON_BIG, (LPARAM)hIcon); return TRUE;
}
下面就是消息开始宏
BEGIN_MESSAGE_MAP()
和消息结束宏
END_MESSAGE_MAP()
#define BEGIN_MESSAGE_MAP(thisClass,BaseClass)\ //传参数(本类名称,父类名称)
const MSGSTRUCT* thisClass::GetMessageMap()const\
{\
return getThisMessageMap();\ //返回本类的静态数组的首地址
}\
const MSGSTRUCT * thisClass::FindMsg(UINT uMsg,WPARAM wParam,LPARAM lParam)\ //查找消息,本类没找到去父类找,当然父类中也是这么个样式
{\
const MSGSTRUCT* pMsg = GetMessageMap();\ //获得本类中静态数组的首地址
if (pMsg != NULL)\
{\
while (pMsg->uMsg != 0)\ //循环查找,这就是为啥每个静态数组中最后一个必须设置一个全是0的消息
{\
if (uMsg == WM_COMMAND)\ //处理WM_COMMAND消息
{\
if (pMsg->uCtrID == LOWORD(wParam) && pMsg->uCod == HIWORD(wParam))\
return pMsg;\
}\
else if (uMsg == WM_NOTIFY)\ //处理WM_NOTIFY消息
{\
if (pMsg->uCtrID == LOWORD(wParam) && pMsg->uCod == ((LPNMHDR)lParam)->code)\
{\
return pMsg;\
}\
}\
else if (pMsg->uMsg == uMsg)\ //处理其它消息
return pMsg;\
pMsg++;\
}\
}\
return BaseClass::FindMsg(uMsg, wParam,lParam);\ //没找到去父类中查找
}\
const MSGSTRUCT* thisClass::getThisMessageMap()\ //本类中的静态数组
{\
static const MSGSTRUCT _messageEntries[] =\
{
//这中间就是要添加的消息ID和函数对应关系的消息映射 #define END_MESSAGE_MAP()\
{0,0,0}\ //这是在静态数组最后添加一个全是0的消息,为了在上面的循环中判断是否到了数组尾
};\
return &_messageEntries[0];\ //返回数组首地址
}
4.定义消息ID和消息函数对应的宏
我只定了四个宏,这些就能包括大部分消息的映射了,如还有其它的以后再弄.
#define ON_WM_MSG(msg,pfn) {msg,0,0,(AFX_PMSG)pfn}, //以WM_xxx开头的消息
#define ON_COMMAND(ctrlid,pfn) {WM_COMMAND,ctrlid,0,(AFX_PMSG)pfn}, //处理WM_COMMAND消息
#define ON_COMMAND_CTRL(code,ctrlid,pfn) {WM_COMMAND,ctrlid,code,(AFX_PMSG)pfn}, //这是处理有些控件通知父窗口是用WM_COMMAND消息通知的,所以要特殊处理下,(编辑控件通知消息就是要用这个消息处理)
#define ON_NOTIFY(code,ctrlid,pfn) {WM_NOTIFY,ctrlid,code,(AFX_PMSG)pfn}, //控件通知父窗口事件消息
头文件差不多就是这些了,下面是.cpp文件的实现
1.在类外实现宏
QDialog::QDialog()
{ } //{WM_CLOSE,0,0,(AFX_PMSG)&OnClose},
//{WM_INITDIALOG, 0, 0, (AFX_PMSG)&OnInitDialog},
//{WM_DESTROY,0,0,(AFX_PMSG)&OnDestroy},
BEGIN_MESSAGE_MAP(QDialog,QWnd)
ON_WM_MSG(WM_DESTROY,&QDialog::OnDestroy) //窗口销毁消息
ON_WM_MSG(WM_CLOSE,&QDialog::OnClose) //窗口关闭消息
ON_WM_MSG(WM_INITDIALOG,&QDialog::OnInitDialog) //对话框初始消息
//添加自己要处理的消息,样式就按上面的来,不要有空行哦
END_MESSAGE_MAP()
因是当作对话框基类,所以就只定了这三个消息,其它消息在子类中去实现,原理和这一个样,就不用书写N多的虚函数让子类去继承
用法就是把你要处理的消息和消息函数通过消息宏放到BEGIN_MESSAGE_MAP和END_MESSAGE_MAP之间
1.创建以模板资源的对话框
BOOL QDialog::CreatedialogX(HINSTANCE hInstance,DWORD lpDlgIDD, QWnd* pParent /*= NULL*/)
{
m_hParent = (pParent != NULL) ? pParent->Gethwnd() : nullptr;
m_hInstance = hInstance;
m_DlgIDD = lpDlgIDD;
m_hWnd = ::CreateDialogParam(hInstance, MAKEINTRESOURCE(lpDlgIDD), m_hParent, (DLGPROC)lpDlgProc, (LPARAM)this);//这要传本类的this指针,要在WM_INITDIALOG消息中取出
if (!m_hWnd)
return FALSE;
HICON hIcon = ::LoadIcon(nullptr, IDI_APPLICATION);
SendMessage(m_hWnd, WM_SETICON, ICON_BIG, (LPARAM)hIcon); return TRUE;
}
2.在静态函数中查寻消息和分发消息
LRESULT QDialog::lpDlgProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
if (uMsg == WM_INITDIALOG)
{
QDialog* pDlg = (QDialog*)lParam;//取出前面创建的时候传进来的this指针,
//如果你要创建自己定义的窗口类,就要在WM_CREATE中捕获,传进来的lParam是一个LPCREATESTRUCT结构指针,this指针在LPCREATESTRUCT的lpCreateParams成员变量中,要切记不要弄错了,不然后果呵呵
if (pDlg!=nullptr && pDlg->m_hInstance != 0)
{
pDlg->m_hWnd = hWnd;
SetWindowLongPtr(hWnd, GWL_USERDATA, (LONG)pDlg);//把this指针设置到类的自定义数据中
}
} /*if (WM_NOTIFY == uMsg)这是我忘记这个消息是如何捕捉的,所以在这里来捕捉,方便写宏
{
if(LOWORD(wParam)==IDC_LIST1 && ((LPNMHDR)lParam)->code==NM_RCLICK)
int n = 0;
}*/ QDialog* pDlg = (QDialog*)GetWindowLongPtr(hWnd, GWL_USERDATA);//从类的自定义数据中取出this指针
if (pDlg!= nullptr)
{
const MSGSTRUCT* pMs = pDlg->FindMsg(uMsg, wParam,lParam);//调用本类的查找消息函数
if (pMs != NULL)
{
return (pDlg->*(pMs->pfn))(wParam, lParam);//找到就返回对应的消息函数
}
} return 0;
}
3,实现你放进静态数组中的自定义消息函数了
LRESULT QDialog::OnInitDialog(WPARAM wParam, LPARAM lParam)
{ return LRESULT();
}
//因对话框是两种形式,所以设置了一个成员变量m_IsModul来判定是否是模态对话框
LRESULT QDialog::OnDestroy(WPARAM wParam, LPARAM lParam)
{
if (m_IsModul)//判断是不是模态对话框
{
return (INT_PTR)::EndDialog(m_hWnd, (INT_PTR)m_lpVoidData);//模态形式销毁对话框
}
PostQuitMessage(0);//非模态形式销毁,即退出程序
return 0;
} LRESULT QDialog::OnClose(WPARAM wParam, LPARAM lParam)
{
if(m_IsModul)
SetActiveWindow(m_hParent);//在销毁对话框之前将父窗口设置为激活状态
return DestroyWindow(m_hWnd);
}
4.模态对话框的实现
INT_PTR QDialog::DoModel(QWnd* pParent,LPVOID lpVoidData/* = 0*/)
{
m_lpVoidData = lpVoidData;//为了传各种数据设定的
m_IsModul = TRUE;
m_hInstance = ::GetModuleHandle(NULL);
m_hParent = pParent->Gethwnd();
INT_PTR nRest=::DialogBoxParam(m_hInstance, MAKEINTRESOURCE(m_DlgIDD), pParent->Gethwnd(), (DLGPROC)lpDlgProc, (LPARAM)this);//要传this指针
//SetActiveWindow(pParent->Gethwnd());//这个要放在关闭模态对话框消息中,放在这对话框已销毁了,所以不起作用
return nRest; //返回你自己设定的数据,
}
5.模态对话框的应用
创建两个类MyDialog,AddDlg,都继承QDialog类
LRESULT MyDialog::OnIDok(WPARAM wParam, LPARAM lParam)
{ TCHAR temStr[] = TEXT("我爱中华");
AddDialog dlg;
INT_PTR nRest=dlg.DoModel(this, LPVOID(temStr));//创建模态对话框,传入一个字符串,当然也可以传别的数据,一般都是传结构体,因为它带的数据最多.
if (nRest != IDCANCEL)
{
//根据你设定的返回数据来接收,我这是传回来一字符串
TCHAR* pString = (TCHAR*)nRest;
::MessageBox(m_hWnd, pString, TEXT("数据接收"), MB_OK);
delete pString;//要删除这个指针,因为传出来的数据是new出来的,要在这删除指针指向的内存,不然就有内存漏泄
}
return 0;
}
6.在对话框初始函数OnInitDialog函数中接收
LRESULT AddDialog::OnInitDialog(WPARAM wParam, LPARAM lParam)
{
if (m_lpVoidData != 0)//不要用lParam,回为lParam是传进来的是this指针
{
//接收传进来的数据
TCHAR* pString = (TCHAR*)m_lpVoidData;
::SetDlgItemText(m_hWnd, IDC_EDIT1, pString);
}
return 0;
}
7.在对话框确定按钮函数中把要传出的数据传出来.
LRESULT AddDialog::OnIDok(WPARAM wParam, LPARAM lParam)
{
TCHAR *pString = new TCHAR[64]; //这要new你想要创建的数据内存,不然窗口销毁了内存也没了,接收的时候就是乱码.
::GetDlgItemText(m_hWnd, IDC_EDIT1, pString, 64); return ::EndDialog(m_hWnd, (INT_PTR)pString);
}
好了,目前就这样了,以后有时间再优化,晕晕糊糊写了好几个小时,还边写边改.有点饿了.
仿MFC消息机制封装对话框窗口类的更多相关文章
- vc++窗口的创建过程(MFC消息机制的经典文章)
一.什么是窗口类 在Windows中运行的程序,大多数都有一个或几个可以看得见的窗口,而在这些窗口被创建起来之前,操作系统怎么知道该怎样创建该窗口,以及用户操作该窗口的各种消息交给谁处理呢?所以VC ...
- duilib底层机制剖析:窗口类与窗口句柄的关联
转载请说明原出处.谢谢~~ 看到群里朋友有人讨论WTL中的thunk技术,让我联想到了duilib的类似技术. 这些技术都是为了解决c++封装的窗口类与窗口句柄的关联问题. 这里是三篇关于thunk技 ...
- MFC关于多线程中传递窗口类指针时ASSERT_VALID出错的另类解决 转
MFC关于多线程中传递窗口类指针时ASSERT_VALID出错的另类解决 在多线程设计中,许多人为了省事,会将对话框类或其它类的指针传给工作线程,而在工作线程中调用该类的成员函数或成员变量等等. ...
- MFC消息机制
何谓消息.消息处理函数.消息映射?消息简单的说就是指通过输入设备向程序发出指令要执行某个操作.具体的某个操作是你的一系列代码.称为消息处理函数. 在SDK中消息其实非常容易理解,当窗口建立后便会有一个 ...
- Win32 DLL和MFC DLL 中封装对话框
现在最常看见的关于DLL的问题就是如何在DLL中使用对话框,这是一个很普遍的关于如何在DLL中使用资源的问题.这里我们从Win32 DLL和MFC DLL两个方面来分析并解决这个问题. ...
- MFC中查找替换对话框CFindReplaceDialog类
void CCFindReplaceDialogView::OnFind() { CFindReplaceDialog* pDlg = new CFindReplaceDialog(); pDlg-& ...
- (转)MFC消息机制
转自:http://blog.csdn.net/kongfuxionghao/article/details/35882533
- MFC学习(四) 消息机制
1 消息机制的要点: 消息队列:先进先出 消息循环:通过循环while,不断的从消息队列中取得队首消息,并分发消息. 消息处理:根据不同的消息类型做不同的处理 事件:事件响应函数 2 消息机制 _tW ...
- MFC消息响应机制分析
---- 摘要: ---- MFC是Windows下程序设计的最流行的一个类库,但是该类库比较庞杂,尤其是它的消息映射机制,更是涉及到很多低层的东西,我们在这里,对它的整个消息映射机制进行了系统的分析 ...
- 窗口的子类化与超类化——子类化是窗口实例级别的,超类化是在窗口类(WNDCLASS)级别的
1. 子类化 理论:子类化是这样一种技术,它允许一个应用程序截获发往另一个窗口的消息.一个应用程序通过截获属于另一个窗口的消息,从而实现增加.监视或者修改那个窗口的缺省行为.子类化是用来改变或者扩展一 ...
随机推荐
- 云服务器通过内网穿透的方式ssh访问内网服务器
云服务器通过内网穿透的方式ssh访问内网服务器 背景 买了一台云服务器,了解到可以通过外部服务器连接到公司内部服务器. 为了加快办公的效率,配置了一下. 以Ubuntu为例. 原文(有删改):http ...
- 中台框架模块开发实践-用 Admin.Core 代码生成器生成通用代码生成器的模块代码
前言 之前分享中台 Admin.Core 的模块代码生成器,陆续也结合群友们的反馈,完善了一些功能和模板上的优化,而本篇将基于此代码生成器生成一个通用代码生成器模块的基本代码 后续再在此代码的基础上进 ...
- 基于RK3588的8K视频解码显示案例分享!引领超高清工业视频时代
8K.4K.2K显示对比 2K分辨率:也称为全高清(FULL HD),它具有1920 x 1080像素的分辨率.这是目前大多数消费者电视和电脑显示器的标准分辨率,可以提供良好的图像质量. 4K分辨率: ...
- windows下rust环境的安装(现在是2023年5月份)
在自己家电脑上安装一下rust,还是遇到一些问题,这里记录一下,免得后面再踩坑. 官方网站 获取主要信息还得靠官网,比如安装软件:) 地址是 https://www.rust-lang.org/zh- ...
- EXPLAIN sql优化方法
select A . id , A . title , B . title from jos_content A left join jos_categories B on A . catid = ...
- [oeasy]python0022_框架标题的制作_banner_结尾字符串_end
结尾字符串(end) 回忆上次内容 python3 的程序是一个 5.3M 的可执行文件 python3 里面存的是 cpu 指令 可以执行的那种 我们可以把指令对应的汇编找到 ...
- ComfyUI进阶:Comfyroll插件 (四)
ComfyUI进阶:Comfyroll插件 (四) 前言: 学习ComfyUI是一场持久战,而Comfyroll 是一款功能强大的自定义节点集合,专为 ComfyUI 用户打造,旨在提供更加丰富和专业 ...
- .Net4.5及.Net Core2.1下的HttpClient使用详解
一.HTTP系列演进 方式 说明 HttpWebRequest .NET早期版本,同步方式 WebClient HttpWebRequest的封装简化版,同步方式 HttpClient .NET4.5 ...
- es6高级~promise
1.Promise对象 Promise 对象用于表示一个异步操作的最终完成 (或失败)及其结果值.其作用是为了解决回调地狱 回调地狱:回调函数的结果作为下一个回调函数的参数时,产生回调链,也称之为回调 ...
- 初看vue3源码
因为工作的原因又回到了vue的领域,需要加深对vue和vue生态的了解 也许平时比较多人手机看别人解析怎么看vue源码的,自己动手看vue源码的还是比较少,这次我想自己动手看看 首先 吧代码获取到本地 ...