设为首页 加入收藏

TOP

MFC程序员的WTL开发指南之ATL界面类(三)
2012-11-04 15:07:19 来源: 作者: 【 】 浏览:802
Tags:MFC 程序员 WTL 开发指南 ATL 界面
  高级消息映射链和嵌入类

  ATL的另一个显著不同之处就是任何一个C++(www.cppentry.com)类都可以响应消息,而MFC只是将消息响应任务分给了CWnd类和CCmdTarget类,外加几个有PreTranslateMessage()方法的类。ATL的这种特性允许我们编写所谓的“嵌入类”,为我们的窗口添加特性只需将该类添加到继承列表中就行了,就这么简单!

  一个基本的带有消息映射链的类通常是模板类,将派生类的类名作为模板的参数,这样它就可以访问派生类中的成员,比如m_hWnd(CWindow类中的HWND成员)。让我们来看一个嵌入类的例子,这个嵌入类通过响应WM_ERASEBKGND消息来画窗口的背景。

template <class T, COLORREF t_crBrushColor>
class CPaintBkgnd : public CMessageMap
{
 public:
  CPaintBkgnd() { m_hbrBkgnd = CreateSolidBrush(t_crBrushColor); }
  ~CPaintBkgnd() { DeleteObject ( m_hbrBkgnd ); }

  BEGIN_MSG_MAP(CPaintBkgnd)
  MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBkgnd)
  END_MSG_MAP()

  LRESULT OnEraseBkgnd(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
  {
   T* pT = static_cast<T*>(this);
   HDC dc = (HDC) wParam;
   RECT rcClient;

   pT->GetClientRect ( &rcClient );
   FillRect ( dc, &rcClient, m_hbrBkgnd );
   return 1; // we painted the background
  }

 protected:
  HBRUSH m_hbrBkgnd;
};

  让我们来研究一下这个新类。首先,CPaintBkgnd有两个模板参数:使用CPaintBkgnd的派生类的名字和用来画窗口背景的颜色。(t_ 前缀通常用来作为模板类的模板参数的前缀)CPaintBkgnd也是从CMessageMap派生的,这并不是必须的,因为所有需要响应消息的类只需使用BEGIN_MSG_MAP宏就足够了,所以你可能看到其他的一些嵌入类的例子代码,它们并不是从该基类派生的。 构造函数和析构函数都相当简单,只是创建和销毁Windows画刷,这个画刷由参数t_crBrushColor决定颜色。接着是消息映射链,它响应WM_ERASEBKGND消息,最后由响应函数OnEraseBkgnd()用构造函数创建的画刷填充窗口的背景。在OnEraseBkgnd()中有两件事需要注意,一个是它使用了一个派生的窗口类的方法(即GetClientRect()),我们如何知道派生类中有GetClientRect()方法呢?如果派生类中没有这个方法我们的代码也不会有任何抱怨,由编译器确认派生类T是从CWindow派生的。另一个是OnEraseBkgnd()没有将消息参数wParam展开为设备上下文(DC)。(WTL最终会解决这个问题,我们很快就可以看到,我保证)

  要在我们的窗口中使用这个嵌入类需要做两件事:首先,将它加入到继承列表:

class CMyWindow : public CWindowImpl<CMyWindow, CWindow, CFrameWinTraits>,
public CPaintBkgnd<CMyWindow, RGB(0,0,255)>

  其次,需要CMyWindow将消息传递给CPaintBkgnd,就是将其链入到消息映射链,在CMyWindow的消息映射链中加入CHAIN_MSG_MAP宏:

class CMyWindow : public CWindowImpl<CMyWindow, CWindow, CFrameWinTraits>,
public CPaintBkgnd<CMyWindow, RGB(0,0,255)>
{
 ...
 typedef CPaintBkgnd<CMyWindow, RGB(0,0,255)> CPaintBkgndBase;

  BEGIN_MSG_MAP(CMyWindow)
  MESSAGE_HANDLER(WM_CLOSE, OnClose)
  MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
  COMMAND_HANDLER(IDC_ABOUT, OnAbout)
  CHAIN_MSG_MAP(CPaintBkgndBase)
  END_MSG_MAP()
 ...
};

  任何CMyWindow没有处理的消息都被传递给CPaintBkgnd。应该注意的是WM_CLOSE,WM_DESTROY和IDC_ABOUT消息将不会传递,因为这些消息一旦被处理消息映射链的查找就会中止。使用typedef是必要地,因为宏是预处理宏,只能有一个参数,如果我们将CPaintBkgnd<CMyWindow, RGB(0,0,255)>作为参数传递,那个“,”会使预处理器认为我们使用了多个参数。

  你可以在继承列表中使用多个嵌入类,每一个嵌入类使用一个CHAIN_MSG_MAP宏,这样消息映射链就会将消息传递给它。这与MFC不同,MFC地CWnd派生类只能有一个基类,MFC自动将消息传递给基类。

  ATL程序的结构

  到目前为止我们已经有了一个完整地主窗口类(即使不完全有用),让我们看看如何在程序中使用它。一个ATL程序包含一个CComModule类型的全局变量_Module,这和MFC的程序都有一个CWinApp类型的全局变量theApp有些类似,唯一不同的是在ATL中这个变量必须命名为_Module。

  下面是stdafx.h文件的开始部分:

// stdafx.h:
#define STRICT
#define VC_EXTRALEAN

#include <atlbase.h> // 基本的ATL类
extern CComModule _Module; // 全局_Module
#include <atlwin.h> // ATL窗口类

  atlbase.h已经包含最基本的Window编程(www.cppentry.com)的头文件,所以我们不需要在包含windows.h,tchar.h之类的头文件。在CPP文件中声明了_Module变量:

// main.cpp:
CComModule _Module;

  CComModule含有程序的初始化和关闭函数,需要在WinMain()中显示的调用,让我们从这里开始:

// main.cpp:
CComModule _Module;

int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hInstPrev,
LPSTR szCmdLine, int nCmdShow)
{
 _Module.Init(NULL, hInst);
 _Module.Term();
}

  Init()的第一个参数只有COM的服务程序才有用,由于我们的EXE不含有COM对象,我们只需将NULL传递给Init()就行了。ATL不提供自己的WinMain()和类似MFC的消息泵,所以我们需要创建CMyWindow对象并添加消息泵才能使我们的程序运行。

// main.cpp:
#include "MyWindow.h"
CComModule _Module;

int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hInstPrev,
LPSTR szCmdLine, int nCmdShow)
{
 _Module.Init(NULL, hInst);

 CMyWindow wndMain;
 MSG msg;

 // Create & show our main window
 if ( NULL == wndMain.Create ( NULL, CWindow::rcDefault, _T("My First ATL Window") ))
 {
  // Bad news, window creation failed
  return 1;
 }

 wndMain.ShowWindow(nCmdShow);
 wndMain.UpdateWindow();

 // Run the message loop
 while ( GetMessage(&msg, NULL, 0, 0) > 0 )
 {
  TranslateMessage(&msg);
  DispatchMessage(&msg);
 }

 _Module.Term();
 return msg.wParam;
}

  上面的代码唯一需要说明的是CWindow::rcDefault,这是CWindow中的成员(静态数据成员),数据类型是RECT。和调用CreateWindow() API时使用CW_USEDEFAULT指定窗口的宽度和高度一样,ATL使用rcDefault作为窗口的最初大小。

  在ATL代码内部,ATL使用了一些类似汇编语言的魔法将主窗口的句柄与相应的CMyWindow对象联系起来,在外部看来就是可以毫无问题的在线程之间传递CWindow对象,而MFC的CWnd却不能这样作。

  这就是我们的窗口:


  我得承认这确实没有什么激动人心的地方。我们将添加一个About菜单并显示一个对话框,主要是为它增加一些情趣。

首页 上一页 1 2 3 4 下一页 尾页 3/4/4
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
分享到: 
上一篇MFC程序员的WTL指南之WTL 界面基类 下一篇ATL布幔下的秘密之窗口类的秘密

评论

帐  号: 密码: (新用户注册)
验 证 码:
表  情:
内  容: