WTL for MFC Programmers 学习笔记(一) Party I ATL GUI Classes

来源:互联网 发布:上古传说单机版mac 编辑:程序博客网 时间:2024/05/16 17:59

Party I ATL GUI Classes

ATL-style templates

class CMyWnd : public CWindowImpl<CMyWnd>

{

...

};

       CMyWndclass CMyWnd语句后即被定义,可以在其后的继承关系列表中使用。子类名(当前声明类)在继承列表中出现的用意在于,实现“编译时虚函数调用”。

template <class T>

class B1

{

public:

    void SayHi()

{

       // 将自身转换成子类

            T* pT = static_cast<T*>(this);   // HUH?? I'll explain this below

 

        pT->PrintClassName();

    }

 

    void PrintClassName() { cout << "This is B1"; }

};

 

class D1 : public B1<D1>

{

    // No overridden functions at all

};

 

class D2 : public B1<D2>

{

    void PrintClassName() { cout << "This is D2"; }

};

 

int main()

{

D1 d1;

D2 d2;

 

    d1.SayHi();    // prints "This is B1"

    d2.SayHi();    // prints "This is D2"

 

    return 0;

}

       此项技术的好处有:

1.       不必要使用对象指针;

2.       节省了虚函数表所占空间;

3.       保证了在运行期调用未初始化虚函数表空间的空指针错误;

4.       所有的函数调用在编译期即明确,能够被优化。

 

ATL Windowing Classes

Class CWindow

{

Public:

       HWND m_wnd;

 

       Operator HWND() { return m_wnd; }

}

是对HWND的封装,只有一个数据成员,其成员函数实现在:

Class CWindowImpl : public  CWindow

{

}

中,包括窗体类注册、窗体子类化、消息映射,及WindowProc()

 

Defining a Window Implementation

       非对话框窗体继承自CWindowImpl。需要包含:

1.       窗体类定义

2.       消息映射

3.       窗体默认样式,叫作窗口特征(window traits)

 

窗体类定义由DECLARE_WND_CLASS  DECLARE_WND_CLASS_EX宏实现。他们都定义了一个封装了WNDCLASSEX结构的ATL结构CWndClassInfo

DECLARE_WND_CLASS用于指定新窗体类的名称其他成员采用默认值;

DECLARE_WND_CLASS_EX除了可以指定窗体类名称还可以指定窗体背景颜色。

当窗体类名称指定为NULL时,ATL会自动生成一个名称。

Class CMyWindow : public CWindowImpl<CMyWindow>

{

Public:

       DECLARE_WND_CLASS(_T(“My Window Calss”))

};

 

       消息映射宏,其被解释成一个switch-case结构,当匹配到正确的handler时调用相应的方法。

Class CMyWindow : public CWindowImpl<CMyWindow>

{

Public:

       DECLARE_WND_CLASS(_T(“My Window Class”))

 

       BEGIN_MSG_MAP(CMyWindow)

       END_MSG_MAP()

};

 

       窗口特征,由窗口类型即窗口类型扩展组合而成,在创建窗体的时候被使用。窗口类型作为窗口特征定义的模板参数,这样调用放不需要为使用正确的窗口类型而烦扰。

Typedef CWinTraits<WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN, WS_EX_APPWINDOW> CMyWindowTraits;

 

Class CMyWindow : public CWindowImpl<CMyWindow, CWindow, CMyWindowTraits>

{

Public:

       DECLARE_WND_CLASS(_T(“My Window Class”))

 

       BEGIN_MSG_MAP(CMyWindow)

       END_MSG_MAP()

};

ATL预定义了一些CWinTraits规范。

 

Filling in the message map

ATL的消息映射对于开发者并不好用,但是WTL在这方面有改进。ATL消息映射不存在像MFC中的特殊消息宏及自动参数解析功能。只有三种类型消息句柄:WM_NOTIFYWM_COMMAND及其他。

Class CMyWindow : public CWindowImpl<CMyWindow, CWindow, CFrameWinTraits>

{

Public:

       DECLARE_WND_CLASS(_T(“My Window Class”))

 

       BEGIN_MSG_MAP(CMyWindow)

              MESSAGE_HANDLER(WM_CLOSE, OnClose)         // WM_MESSAGE

              MESSAGE_HANDLER(WM_DESTROY, OnDestroy)

              COMMAND_ID_HANDLER(IDC_ABOUT, OnAbout)       // WM_COMMAND

                                                                                                  // WM_NOTIFY

       END_MSG_MAP()

 

       LRESULT OnClose(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)

{

       DestroyWindow();

       Return 0;

}

LRESULT OnDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)

{

       PostQuitMessage(0);

       Return 0;

}

LRESULT OnAbout(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled)

{

       MessageBox(_T(“Sample ATL Window”), _T(“About MyWindow”));

       Return 0;

}

每个消息对应着wParamlParam参数,如果需要的话。这两个参数需要调用者自己解析。第四个参数,ATL在调用前将其置true,如果希望退后后由默认WindowProc处理,则可将其置false。这中机制和MFC中不同,在MFC通过显示调用基类中对应消息处理方法来实现。

       COMMAND_HANDER宏将消息参数解析了,同样的WM_NOTIFY的消息参数也解析了。

 

Advanced Message Maps and Mix-in Classes

       ATL主要特点之一是每个类都可以处理消息,而MFC中只有CWndCCmdTarget,以及一些含有PreTranslateMessage函数的类才能处理。这一特性使我们可以方便得开发嵌入类,加入窗体的继承列表为窗体添加新的特性。

       带有消息处理的基类一般把子类作为模板参数,这样在基类中就可以使用子类的成员(如CWindow中的HWND)。

Template<class T, COLORRE t_crBrushColor> // 注意,第二个参数是值

Class CPaintBkgnd

{

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);  // 编译时,如果T不含有GetClientRect方法就会报错,保证了子类必须有该方法

       FillRect(dc, &rcClient, m_hbrBkgnd);

       Return 1;

}

 

Protected:

       HBRUSH m_hbrBkgnd;

};

 

       如何使用嵌入类呢,首先,把嵌入类加入子类的继承列表中:

Class CMyWindow : public CWindowImpl<CMyWindow, CWindow, CFrameWinTraits>,

                            Public CPaintBkgnd<CMyWindow, RGB(0,0,255)>

       其次,需要从子类将消息传递到父类中,这叫做链消息映射。使用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) // 以上三个消息将不会往下传递,因为都已处理,即bHandled==true

              CHAIN_MSG_MAP(CPaintBkgndBase)       // 预处理宏,只有一个参数,所以前面需要typedef,否则从“,”认为是多个参数

       END_MSG_MAP()

};

       有别于MFCATL链消息机制,使得子类可以继承自多个嵌入类,消息可以传递到过个类中,而MFC只能通过显示调用基类(只有一个)的方法来传递未处理的消息。

 

Structure of an ATL EXE

In VC 6

       一个ATL可执行文件包含一个CComModule类型全局变量_Modulestdafx.h中如:

// stdafx.h:

#define STRICT

#define WIN32_LEAN_AND_MEAN

 

#include <atlbase.h>        // Base ATL classes

extern CComModule _Module;  // Global _Module

#include <atlwin.h>         // ATL windowing classes

       Atlbase.h包含基本Windows头文件,所以不需要另外包含windows.htchar.h等,在cpp文件中声明:

// main.cpp:

CComModule _Module;

       CComModule需要显示初始化和销毁,:

// main.cpp

CComModule _Module;

Int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hInstPrev, LPSTR szCmdLine, int nCmdShow)

{

       _Module.Init(NULL, hInst);

       _Module.Term();

}

       Init()中第一个参数只在Com服务器的时候使用,EXE的话传NULL就可。ATL本身不提供如在MFC中的WinMain()或消息泵机制,所以需要创建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 Firsh 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;

}

 

In VC7

       ATL 7Module管理的代码分散到多个类中。ATL头文件中自动声明了module全局实例,并且Init()Term()方法也自动调用了。

// stdafx.h:

#define STRICT

#define WIN32_LEAN_AND_MEAN

 

#include <atlbase.h>        // Base ATL classes

#include <atlwin.h>         // ATL windowing classes

      

 

// main.cpp:

#include "MyWindow.h"

 

int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hInstPrev,

                   LPSTR szCmdLine, int nCmdShow)

{

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);

        }

 

    return msg.wParam;

}

 

Dialogs in ATL

       ATL中有两种对话框类CDialogCAxDialog。用CDialogImpl来实现About对话框。对话框实现和Frame窗体实现相似,仅有两点差异:

1.       基类是CDialogImple而不是CWindowImple

2.       需要定义一个共有的IDD来保存对话框的资源ID.

class CAboutDlg : public CDialogImpl<CAboutDlg>

{

public:

    enum { IDD = IDD_ABOUT };

 

    BEGIN_MSG_MAP(CAboutDlg)

        MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog)

        MESSAGE_HANDLER(WM_CLOSE, OnClose)

        COMMAND_ID_HANDLER(IDOK, OnOKCancel)

        COMMAND_ID_HANDLER(IDCANCEL, OnOKCancel)

    END_MSG_MAP()

 

    LRESULT OnInitDialog(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)

    {

        CenterWindow();

        return TRUE;    // let the system set the focus

    }

 

    LRESULT OnClose(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)

    {

        EndDialog(IDCANCEL);

        return 0;

    }

 

    LRESULT OnOKCancel(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled)

    {

        EndDialog(wID);

        return 0;

    }

};

       没有内置OK/Cancel按钮的消息处理,都必须手工编写。

 

       现在回到CMyWindow,添加菜单,并响应点击事件,弹出对话框:

class CMyWindow : public CWindowImpl<CMyWindow, CWindow, CFrameWinTraits>,

                  public CPaintBkgnd<CMyWindow,RGB(0,0,255)>

{

public:

    BEGIN_MSG_MAP(CMyWindow)

        MESSAGE_HANDLER(WM_CREATE, OnCreate)

        COMMAND_ID_HANDLER(IDC_ABOUT, OnAbout)

        // ...

        CHAIN_MSG_MAP(CPaintBkgndBase)

    END_MSG_MAP()

 

    LRESULT OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)

    {

    HMENU hmenu = LoadMenu ( _Module.GetResourceInstance(),  // _AtlBaseModule in VC7

                             MAKEINTRESOURCE(IDR_MENU1) );

 

        SetMenu ( hmenu );

        return 0;

    }

 

    LRESULT OnAbout(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled)

    {

    CAboutDlg dlg;

 

        dlg.DoModal();

        return 0;

    }

    // ...

};

       MFC中模式对话框不一样,MFC中将父窗体句柄从对话框构造函数传递。ATL中可以通过DoModal()的第一个参数参数传递父窗体,或者不指定父窗体,ATL调用GetActiveWindow()获取父窗体。

原创粉丝点击