WTL for MFC Programming实践篇 --- 一个自定义ComboBox的移植过程(上)

 

WTL for MFC Programming实践篇

                   --- 一个自定义ComboBox的移植过程

                   --- 蜗牛手记

 

     现在有一个MFC写的自定义ComboBox打算移植到WTL上,于是根据WTL的书写方法修改了程序,就得到下面的代码:

Class CComboBoxEx : public CComboBox

{

protected:

     void OnDrawItem(UINT wParam, LPDRAWITEMSTRUCT lpDrawItemStruct);

public:

     BEGIN_MSG_MAP_EX(CComboBoxEx)

         MSG_OCM_DRAWITEM(OnDrawItem)

     END_MSG_MAP()

}

 

Class CMainDlg : public CDialogImpl< CMainDlg >

{

Protected:

         CComboBoxEx        m_cmbEx;

Public:

     BEGIN_DDX_MAP(CPageConfigFont)

         DDX_CONTROL_HANDLE(IDC_COMBOBOXEX, m_cmbEx);

     END_DDX_MAP()

          BEGIN_MSG_MAP_EX(CPageConfigFont)

              MSG_WM_INITDIALOG(OnInitDialog)

              REFLECT_NOTIFICATIONS()

         END_MSG_MAP()

}

 

如何生成以上代码及代码的含义,原书都有介绍,由于不是本文的重点,不再一一解释。

要说的是,在WTL 7.1中添加了DDX_CONTROL_HANDLE宏,可以用来设置控件,与DDX_CONTROL不同的是,它不要求控件类由CWindowImpl派生,即不需要包含SubclassWindow()函数,这样我们才可以使用DDX来设置我们从CComboBox派生的类(听上去很有道理,其实却是在MFC编程习惯带动下错误思维)。

当然,要实现还有一个小小的问题,DDX_CONTROL_HANDLE宏需要我们的类包含一个操作符“=”,怎么写这个函数呢?参看了一下基类的实现方法:

     CComboBoxExT< TBase >& operator =(HWND hWnd)

     {

         m_hWnd = hWnd;

         return *this;

     }

参看WTL文件<atlctrls.h>

原来只是将m_hWnd赋值,于是我们在我们的类中添加如下的代码:

CComboBoxEx& operator=(HWND hWnd)

{

     m_hWnd = hWnd;

     return *this;

}

于是编译通过了。(殊不知潜在的错误就这样被深深的埋起来了)

可是为什么DDX_CONTROL_HANDLE宏需要我们的类包含操作符“=”呢?我们来看看DDX_CONTROL_HANDLE宏是怎么实现的:

整个DDX_MAP其实是定义了一个DoDataExchange函数,BEGIN_DDX_MAP宏定义了函数头,而END_DDX_MAP定义了函数尾,中间一项项的DDX定义函数的具体内容,而当你在代码中定义DDX_MAP的时候就等于重载了CWinDataExchange::DoDataExchange()函数,具体代码如下:

#define BEGIN_DDX_MAP(thisClass) /

     BOOL DoDataExchange(BOOL bSaveAndValidate = FALSE, UINT nCtlID = (UINT)-1) /

     { /

         bSaveAndValidate; /

         nCtlID;

#define END_DDX_MAP() /

         return TRUE; /

     }

参看WTL文件<atlddx.h>

对于DDX_CONTROL_HANDLE宏,它其实是调用了CWinDataExchange:: DDX_Control_Handle函数,具体代码如下:

// Simple control attaching (for HWND wrapper controls)

     template <class TControl>

     void DDX_Control_Handle(UINT nID, TControl& ctrl, BOOL bSave)

     {

         if(!bSave && ctrl.m_hWnd == NULL)

         {

              T* pT = static_cast<T*>(this);

              ctrl = pT->GetDlgItem(nID);

         }

     }

参看WTL文件<atlddx.h>

正如上面的代码,DDX_CONTROL_HANDLE宏是直接将ID所对应的窗体句柄直接赋值给DDX所链接的控件类,于是我们在DDX_MAP中定义的语句与下面的语句是等价的:

m_cmbEx = this->GetDlgItem(IDC_COMBOBOXEX);

所以要想使上面的语句能够使用,重载操作符就变成了一个解决问题的好办法,这就是DDX_CONTROL_HANDLE宏需要我们的类包含操作符“=”的原因。

到这里,我们已经知道了为什么,也作了应该做的事,移植的工作就剩下测试了。当然如果你熟悉WTL或者仔细看了上面的代码,也许会发现有一个很大的问题潜伏着。可是我们是MFC的程序员,习惯用MFC的方法去思考,于是奇怪的事情在测试的时候发生了。

运行一切正常,只是我们在重画函数中的代码没有运行,换句话说,就是重画事件没有被触发。

为什么?我们的所有代码都是按照正确的方法写成的,在CComboBoxExMSG_MAP中添加MSG_OCM_DRAWITEM宏来映射重画事件,在CMainDlgMSG_MAP中添加REFLECT_NOTIFICATIONS()宏。

该做得都做了。为什么不行呢?

在原书中提到,使用 DEFAULT_REFLECTION_HANDLER来处理缺省的反射事件,难道因为缺少这个宏吗?虽然这不是一个符合逻辑的想法,可是现在也把它拿来当活马医一医了。

于是我们在原来的类中添加这个宏,结果错误出现了,提示没有DefaultReflectionHandler函数的定义,哦?这是什么意思啊?我们来查查原码:

#define DEFAULT_REFLECTION_HANDLER() /

     if(DefaultReflectionHandler(hWnd, uMsg, wParam, lParam, lResult)) /

         return TRUE;

参看ATL文件<atlwin.h>

原来DEFAULT_REFLECTION_HANDLER宏只是调用DefaultReflectionHandler函数,那么这个函数又是何许人也呢?DefaultReflectionHandlerCWindowImplRoot的成员函数,也可以说是CWindowImpl的成员函数,因为CWindowImplCWindowImplBase派生,而CWindowImplBaseCWindowImplRoot派生,DefaultReflectionHandler函数其实是对API函数DefWindowProc的封装,不过它只限于处理OCM_的事件。如下面的代码:

template <class TBase>

BOOL CWindowImplRoot< TBase >::DefaultReflectionHandler(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT& lResult)

{

     switch(uMsg)

     {

     case OCM_COMMAND:

     case OCM_NOTIFY:

     case OCM_PARENTNOTIFY:

     case OCM_DRAWITEM:

     case OCM_MEASUREITEM:

     case OCM_COMPAREITEM:

     case OCM_DELETEITEM:

     case OCM_VKEYTOITEM:

     case OCM_CHARTOITEM:

     case OCM_HSCROLL:

     case OCM_VSCROLL:

     case OCM_CTLCOLORBTN:

     case OCM_CTLCOLORDLG:

     case OCM_CTLCOLOREDIT:

     case OCM_CTLCOLORLISTBOX:

     case OCM_CTLCOLORMSGBOX:

     case OCM_CTLCOLORSCROLLBAR:

     case OCM_CTLCOLORSTATIC:

         lResult = ::DefWindowProc(hWnd, uMsg - OCM__BASE, wParam, lParam);

         return TRUE;

     default:

         break;

     }

     return FALSE;

}

看到这里,如果想添加DEFAULT_REFLECTION_HANDLER宏,控件类就要由CWindowImpl派生。为了测试把死马当活马医的想法,我们把类的定义改为如下这样:

class CComboBoxEx:public CWindowImpl< CComboBoxEx, CComboBox>

于是,添加DEFAULT_REFLECTION_HANDLER宏得操作通过了编译,但是事实证明,不合逻辑的想法很难带来正确的结果,不仅重画事件没有被触发,修改后,在控件类析构时碰到了ATL的断言。

错误提示是,类在窗体句柄销毁之前被析构。

这个错误到让我们想到原书中提到的一个WTL特性,WTL不会自动销毁窗体句柄,需要自己手工Detach()窗体句柄。既然这样,我们又添加了下面的代码:

~CComboBoxEx() {

     Detach();

}

虽然,没有Attach()Detach()感觉有点怪,可是毕竟ATL的断言不会出现了。但是,问题并没有解决,重画事件还是没有被触发。难道是CMainDlg没有反射事件回来?看看用来反射事件的REFLECT_NOTIFICATIONS宏的代码:

#define REFLECT_NOTIFICATIONS() /

     { /

         bHandled = TRUE; /

         lResult = ReflectNotifications(uMsg, wParam, lParam, bHandled); /

         if(bHandled) /

              return TRUE; /

     }

              参看ATL文件<atlwin.h>

REFLECT_NOTIFICATIONS宏调用的是函数CWindowImplRoot::ReflectNotifications。这个函数通过参数取得发送事件控件的窗体句柄,并通过该句柄将事件发还给控件,代码如下:

template <class TBase>

LRESULT CWindowImplRoot< TBase >::ReflectNotifications(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)

{

     HWND hWndChild = NULL;

 

     switch(uMsg)

     {

     case WM_COMMAND:

         if(lParam != NULL) // not from a menu

              hWndChild = (HWND)lParam;

         break;

     case WM_NOTIFY:

         hWndChild = ((LPNMHDR)lParam)->hwndFrom;

         break;

     case WM_PARENTNOTIFY:

         switch(LOWORD(wParam))

         {

         case WM_CREATE:

         case WM_DESTROY:

              hWndChild = (HWND)lParam;

              break;

         default:

              hWndChild = GetDlgItem(HIWORD(wParam));

              break;

         }

         break;

     case WM_DRAWITEM:

         if(wParam)    // not from a menu

              hWndChild = ((LPDRAWITEMSTRUCT)lParam)->hwndItem;

         break;

     case WM_MEASUREITEM:

         if(wParam)    // not from a menu

              hWndChild = GetDlgItem(((LPMEASUREITEMSTRUCT)lParam)->CtlID);

         break;

     case WM_COMPAREITEM:

         if(wParam)    // not from a menu

              hWndChild = GetDlgItem(((LPCOMPAREITEMSTRUCT)lParam)->CtlID);

         break;

     case WM_DELETEITEM:

         if(wParam)    // not from a menu

              hWndChild = GetDlgItem(((LPDELETEITEMSTRUCT)lParam)->CtlID);

         break;

     case WM_VKEYTOITEM:

     case WM_CHARTOITEM:

     case WM_HSCROLL:

     case WM_VSCROLL:

         hWndChild = (HWND)lParam;

         break;

     case WM_CTLCOLORBTN:

     case WM_CTLCOLORDLG:

     case WM_CTLCOLOREDIT:

     case WM_CTLCOLORLISTBOX:

     case WM_CTLCOLORMSGBOX:

     case WM_CTLCOLORSCROLLBAR:

     case WM_CTLCOLORSTATIC:

         hWndChild = (HWND)lParam;

         break;

     default:

         break;

     }

 

     if(hWndChild == NULL)

     {

         bHandled = FALSE;

         return 1;

     }

 

     ATLASSERT(::IsWindow(hWndChild));

     return ::SendMessage(hWndChild, OCM__BASE + uMsg, wParam, lParam);

}

              参看ATL文件<atlwin.h>

     我们感兴趣的是最后一句,控件接收到的是ID = OCM__BASE + WM_DRAWITEM的消息,那么我们可以让控件直接接收消息(OCM__BASE + WM_DRAWITEM),用于取代使用不起作用的MSG_OCM_DRAWITEM。于是有了下面的代码:

     MESSAGE_HANDLER_EX(OCM__BASE + WM_DRAWITEM, OnDrawItem)

     但是结果还是一样 - 重画事件没有被触发。

幸亏我们有了新的发现,否则有可能就没由信心解决这个问题了。我们在CMainDlg中添加了WM_DRAWITEM事件,结果捕抓到了CComboBoxEx的重画事件,这说明CComBoxEx的重画事件发出了,但不知什么原因没有反射回控件。于是我们在CMainDlg::OnDrawItem()中添加了

SendMessage(m_cmbEx.m_hWnd, OCM__BASE + WM_DRAWITEM, 0, 0)

以取代REFLECT_NOTIFICATIONS宏所做的自动反射,结果发现,事件还是没有收到。难道WTL事件处理出了问题?我们又为CComboBoxEx添加了非反射的事件WM_PAINT,结果发现WM_PAINT事件也没有被触发!!!

CComboBoxEx根本无法收到任何事件!!!!!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
目录 WTL for MFC Programmers, Chinese Version Prologue WTL for MFC Programmers, Chinese Version PrologueWTL for MFC Programmers, Chinese Version Prologue WTL for MFC Programmers, Chinese Version Prologue WTL for MFC Programmers, Chinese Version Prologue WTL for MFC Programmers, Chinese Version PrologueWTL for MFC Programmers, Chinese Version PrologueWTL for MFC Programmers, Chinese Version Prologue WTL for MFC Programmers, Chinese Version PrologueWTL for MFC Programmers, Chinese Version Prologue WTL for MFC Programmers, Chinese Version Prologue WTL for MFC Programmers, Chinese Version PrologueWTL for MFC Programmers, Chinese Version PrologueWTL for MFC Programmers, Chinese Version PrologueWTL for MFC Programmers, Chinese Version Prologue WTL for MFC Programmers, Chinese Version PrologueWTL for MFC Programmers, Chinese Version PrologueWTL for MFC Programmers, Chinese Version PrologueWTL for MFC Programmers, Chinese Version Prologue WTL for MFC Programmers, Chinese Version PrologueWTL for MFC Programmers, Chinese Version PrologueWTL for MFC Programmers, Chinese Version PrologueWTL for MFC Programmers, Chinese Version PrologueWTL for MFC Programmers, Chinese Version Prologue WTL for MFC Programmers, Chinese Version Prologue WTL for MFC Programmers, Chinese Version Prologue WTL for MFC Programmers, Chinese Version PrologueWTL for MFC Programmers, Chinese Version Prologue WTL for MFC Programmers, Chinese Version Prologue WTL for MFC Programmers, Chinese Version Prologue ................................ ........................... 6 WTL for MFC Programmers, Part I - ATL GUI Classes ................................ ........................... 8 README.TXTREADME.TXT README.TXTREADME.TXT README.TXTREADME.TXT ................................ ................................ ................................ ......................... 8 对本系列文章的总体介绍 对本系列文章的总体介绍 对本系列文章的总体介绍 ................................ ................................ ................................ ....... 9 对第一章的简单介绍 对第一章的简单介绍 ................................ ................................ ................................ ............. 11 ATL ATL ATL 背景知识 ................................ ................................ ................................ ....................... 11 ATLWTL 的发展历史 ................................ ................................ ......................... 12 ATL-style 模板 ................................ ................................ ................................ ............. 12 ATL ATL ATL 窗口类 ................................ ................................ ................................ ........................... 15 定义一个窗口的实现 定义一个窗口的实现 ................................ ................................ ................................ ............. 17 填写消息映射链 ................................ ................................ ................................ ............. 19 高级消息映射链和嵌 高级消息映射链和嵌 入类 ................................ ................................ ................................ ..... 21 ATLATL 程序的结构 ................................ ................................ ................................ .................... 24 ATLATL 中的对话框 ................................ ................................ ................................ .................... 27 WTL for MFC Programmers, Part II - WTL GUI Base Classes ................................ ............. 32 对第二部分的介绍 对第二部分的介绍 ................................ ................................ ................................ ................. 32 WTL WTL WTL WTL 总体印象 ................................ ................................ ................................ ...................... 32 开始写 WTLWTLWTL程序 ................................ ................................ ................................ .................. 33 WTL WTL WTL WTL 对消息映射的增强 ................................ ................................ ................................ ...... 36 从 WTLWTLWTL的应用程序生成向导能得到什么 的应用程序生成向导能得到什么 的应用程序生成向导能得到什么 ................................ ................................ .......... 42 使用向导的整个过程 ................................ ................................ ................................ ..... 42 查看生成的代码 ................................ ................................ ................................ ............. 44 CMessageLoop CMessageLoop CMessageLoop CMessageLoop CMessageLoop CMessageLoop CMessageLoop CMessageLoop 的内部实现 ................................ ................................ ................................ .. 47 CFrameWindowImpl CFrameWindowImpl CFrameWindowImpl CFrameWindowImpl CFrameWindowImpl CFrameWindowImpl CFrameWindowImpl CFrameWindowImpl CFrameWindowImpl CFrameWindowImpl CFrameWindowImpl CFrameWindowImpl CFrameWindowImpl 的内部实现 ................................ ................................ ......................... 48 回到前面的时钟程序 回到前面的时钟程序 ................................ ................................ ................................ ............. 50 界面元素的自动更新 界面元素的自动更新 (UI Updating)(UI Updating)(UI Updating)(UI Updating) (UI Updating) (UI Updating)(UI Updating)(UI Updating)(UI Updating) (UI Updating) ................................ ................................ ...................... 51 添加控制时钟的新菜单项 ................................ ................................ ............................. 52 调用 UIEnable()................................ ................................ ................................ ............ 53 消息映射链中最后需要注意的地方 消息映射链中最后需要注意的地方 消息映射链中最后需要注意的地方 消息映射链中最后需要注意的地方 ................................ ................................
下面是网络收集 WTL for MFC Programmers, Chinese Version Prologue WTL for MFC Programmers, Part I - ATL GUI Classes - WTL WTL for MFC Programmers, Part II - WTL GUI Base Classes - WTL WTL for MFC Programmers, Part III - Toolbars and Status Bars - WTL WTL for MFC Programmers, Part IV - Dialogs and Controls - WTL WTL for MFC Programmers, Part IX - GDI Classes, Common Dialogs, and Utility Classes - WTL WTL for MFC Programmers, Part V - Advanced Dialog UI Classes - WTL WTL for MFC Programmers, Part VI - Hosting ActiveX Controls - WTL WTL for MFC Programmers, Part VII - Splitter Windows - WTL WTL for MFC Programmers, Part VIII - Property Sheets and Wizard 由于工作的需要经常开发一些COM组件,在要求不能使用MFC的场合就是用ATLATL提供了对窗口的面向对象地封装和简单的消息映射机制,但是ATL过于简单,用它开发应用程序几乎不可能。要想让ATL具备界面框架解决方案的功能还需要做很多事情,幸运的是WTL就做了这些事情。WTL是个很奇特的东西,它由微软公司一群热情的程序员维护,它从未出现在微软的官方产品名单上,但可以从微软的官方网站下载最新的WTL。它没有正式的文档支持,用WTL做关键字在MSDN中检索只能得到0个结果,但是全世界的开发网站上都有针对WTL的讨论组和邮件列表,任何问题都会得到热情的解答。我认真地对比了MFCWTL,发现二者有很多相通之处,MFC的功能几乎都能在WTL中实现,只是方法不同而已。我几乎不费吹灰之力就将以前写的一个MFC程序用WTL改写了,使用静态链接的WTL程序比使用动态链接的MFC程序还要小,资源占用只有MFC程序的一半。 但是一时的热情不能解决文档缺乏的困扰,虽然网上有很多使用WTL的例子和说明文章,几乎把MFC能实现的各种稀奇古怪的效果都实现了,但都是着眼于局部问题得解决,缺乏系统地全面地介绍WTL的文章。就在这个时候我看到了迈克尔.敦(Michael Dunn)的“WTL for MFC Programmers”系列文章,我的感觉和1995年我第一次见到MSDN时一样,几乎是迫不及待地将其读完,同时也萌发了将其翻译成汉语的冲动。于是给Michael写了封邮件,希望能够得到授权将他的文章翻译成汉语(事实上在这之前我已经翻译了两章了)。在得到授权确认后才发现这个工作是多么的困难,但为时已晚,只能硬着头皮撑下去。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值