科技行者

行者学院 转型私董会 科技行者专题报道 网红大战科技行者

知识库

知识库 安全导航

至顶网软件频道ATL的GUI程序设计(三)

ATL的GUI程序设计(三)

  • 扫一扫
    分享文章到微信

  • 扫一扫
    关注官方公众号
    至顶头条

本章的内容也可以算是本书的核心部分——如果你要进行ATL的GUI程序设计的话,就必须将ATL的窗口类设计理念了然于心。

作者:李马 来源:CSDN 2007年9月24日

关键字: ghost ATL 程序设计 GUI

  • 评论
  • 分享微博
  • 分享邮件

在本页阅读全文(共2页)

第三章 ATL的窗口类

CWindowImpl、CWindow、CWinTraits,ATL窗口类的奥秘尽在此三者之中。在本章里,李马将为你详细解说它们的使用方法。另外,本章的内容也可以算是本书的核心部分——如果你要进行ATL的GUI程序设计的话,就必须将ATL的窗口类设计理念了然于心。

窗口的组成

把ATL的窗口类撇开不谈先。我在上一章中提到:窗口类并非任何一种OOP语言中的类——它所包括的并不是通称的属性和方法(在C++中称作成员变量和成员函数),而是属性和响应。现在是解释这句话的时候了。

所谓窗口的属性,无非是窗口的样式(style)、背景画刷(brush)、图标(icon)、光标(cursor)……等元素。你可以从WNDCLASS及WNDCLASSEX中找到它们。需要特别指出的是,窗口的样式事实上包括窗口类的样式和窗口实例的样式,窗口类的样式在注册窗口类之前经由WNDCLASS::style或WNDCLASSEX::style指定,而窗口实例的样式则是在创建窗口(CreateWindow/CreateWindowEx)的时候指定的。

对于窗口的响应,即是指窗口收到某消息后的处理。(在VB、Delphi等RAD环境中,处理窗口的响应亦称作窗口的事件处理。)对于SDK而言,为窗口提供响应也就是为窗口类提供一个回调函数,在回调函数中对我们感兴趣的窗口消息进行特殊处理,譬如上一章中针对WM_DESTROY和WM_PAINT的处理。

另外,我们在进行Win32程序设计的时候,往往还需要对窗口进行操作,譬如ShowWindow和UpdateWindow——姑且让我称之为“方法”。

属性、方法、事件,这回这哥仨算齐了。我们在对窗口进行C++封装时,需要考虑的也正是这三者。自然,依据OO的理念,我们可以很简单地将句柄作为成员变量,将方法作为成员函数,然后将事件经由某种特定的消息分流手段移交给各个成员函数进行响应处理,加之对不同种类的窗口使用继承进行区分——这就是MFC的封装做法。大家如果有兴趣的话,可以打开MFC的afxwin.h看一看CWnd类的代码。

ATL窗口类的活版封装

MFC的CWnd是一个冗长得有些过分的类。究其原因,窗口类的封装理念决定了窗口类的消息分流,而消息分流则决定了类的代码篇幅。如果你已经打开了afxwin.h文件,就可以发现CWnd花了很大的篇幅在“On”开头的事件响应函数上。其实在我们进行Win32程序设计的时候,真正感兴趣的事件没有几个,所以说“万能”势必造就冗长。

另外,考虑MFC的诞生年代,所以对于窗口的封装只是采用了C++的低端特性——例如薄层的封装和单向继承。(题外话:而且MFC中还存在着一些诸如CString、CArray、CList之类的工具,盖因其时STL还未标准化之故。)随着MFC的发展,任凭它做出任何优化,也无法避免当初架构理念带来的效率阴影和偏差。

ATL的诞生年代晚于MFC,使之能够有机会使用C++的高端特性,也就是模板和多重继承。于是,它使用了一种全新的封装理念:将属性、方法、事件分别独立出来,然后利用模板和多重继承的特性将这三者根据需要而组合在一起——打个比方来说,如果MFC的窗口封装是雕版印刷术,那么ATL的窗口封装就是活版印刷术。以上一章的CHelloATLWnd类为例,它的继承层次如下图:

这是一个稍显冗长的继承链,不过我并不打算对它进行详细的解说。在此,我只请你看这个继承层次的最底层和最上层。从最底层来看,CHelloATLWnd继承自CWindowImpl,CWindowImpl有三个模板参数:T、TBase、TWinTraits。再看最上层,CWindowImplRoot继承自TBase和CMessageMap。T参数即是你所继承下来的子类名,通常用于编译期的虚函数机制(后边我会对这一机制进行介绍);TBase参数为对窗口方法和句柄的封装;TWinTraits是窗口样式的类封装;CMessageMap是对窗口事件响应的封装。

下面,就让李马来逐一将这些组成部分介绍给你吧。

窗口样式的封装

窗口样式通常由CWinTraits类封装,这个类很简单,如下:

/////////////////////////////////////////////////////////////////////////////
// CWinTraits - Defines various default values for a window

template <DWORD t_dwStyle = 0, DWORD t_dwExStyle = 0>
class CWinTraits
{
public:
    static DWORD GetWndStyle(DWORD dwStyle)
    {
        return dwStyle == 0 ? t_dwStyle : dwStyle;
    }
    static DWORD GetWndExStyle(DWORD dwExStyle)
    {
        return dwExStyle == 0 ? t_dwExStyle : dwExStyle;
    }
};

这个类有两个模板参数:dwStyle和dwExStyle,也就是CreateWindowEx中要用到的那两个样式参数。在CHelloATLWnd::Create(其实也就是CWindowImpl::Create)调用的时候,窗口的样式就是由CWinTraits::GetWndStyle/CWinTraits::GetWndExStyle决定的。

另外,ATL还为常用的窗口样式提供了几个typedef,如CControlWinTraits、CFrameWinTraits、CMDIChildWinTraits。在你需要它们这些特定样式或者需要对它们进行扩展的时候,可以直接进行使用或者使用CWinTraitsOR类来进行进一步的样式组合,这里我就不多介绍了。

窗口方法的封装

说白了,窗口方法的封装其实就是把窗口句柄和常用的窗口操作API函数(也就是那些第一个参数为HWND类型的API函数)进行一层薄薄的绑定。这样做的好处有二:第一,使代码更有逻辑性,符合OO的设计理念;第二,在对SendMessage进行封装后,可以增加对消息参数的类型检查。

CWindow类的内容我就不列出了,因为它同样十分冗长,大家可以参看atlwin.h的相关内容。在这里我仅对其中的几个地方进行解说:

  • 它只有一个非static的成员变量,也就是窗口的句柄m_hWnd。这样做的好处是使得CWindow类的对象占用最小的资源,同时给程序员提供最大的自由度。与MFC的CWnd类相比,CWindow的优点体现得尤为明显。CWnd之中还存在着一些MFC Framework要用到的东西,比如RTTI信息等等。此外,MFC内部还会为每个窗口句柄维护一个相对应的CWnd对象,形成一个对象链,这样程序员可以通过GetDlgItem获取CWnd类的指针,但是这同时也为系统增加了很多额外的负担。
  • CWindow提供了对operator=操作符的重载,这样程序员可以直接将一个HWND赋给一个CWindow对象。
  • CWindow::Attach/CWindow::Detach提供了CWindow对象与HWND的绑定/解除绑定功能。
  • CWindow提供了对operator HWND类型转换操作符的重载,这样在用到HWND类型变量的时候,可以直接使用CWindow对象来代替。

有了CWindow类之后,如果你需要对窗口进行更多的操作,就可以对其进行继承,例如CButton、CListBox、CEdit等等。这样一来,代码的复用性就大大提高了。

    • 评论
    • 分享微博
    • 分享邮件
    邮件订阅

    如果您非常迫切的想了解IT领域最新产品与技术信息,那么订阅至顶网技术邮件将是您的最佳途径之一。

    重磅专题
    往期文章
    最新文章