zoukankan      html  css  js  c++  java
  • 仿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);
    }

    好了,目前就这样了,以后有时间再优化,晕晕糊糊写了好几个小时,还边写边改.有点饿了.

    签名:GreenLeaf1976
  • 相关阅读:
    zlib 2.1.8 编译遇到的问题以及解决方法
    Golang简单日志类
    Golang获得执行文件的当前路径
    Golang的session管理器
    cocos2dx spine之二 :spine变色
    cocos2dx spine之一 :spine缓存 (c++ & lua)
    动态规划
    动态规划
    数学
    [Offer收割]编程练习赛3
  • 原文地址:https://www.cnblogs.com/greenleaf1976/p/14780317.html
Copyright © 2011-2022 走看看