ATL GUI (一)

从今天开始我要进去GUI的世界了。。。

当然还是ATL,MFC我已经厌倦了,不过工作上一定要用就没办法。

要使用ATL的UI功能,当然是使用WTL

工欲善其事,必先利其器,当然要装全你的东西,WTL的sdk你一定要有

废话不说勒,let's GO

今天就先说说ATL的东西吧,

先看个诡异的东西,以前也有过,但是可能以后经常出现,所以要重点的
提一下。

class  CMyWnd : public CWindowImpl<CMyWnd>
{
    ...
}; 

这个东西,我一直觉得诡异,但怎么会编译通过呢
c++语法的解释是什么部分定义,什么要使用递归继承列表,反正搞不明白,也
不用搞明白,知道可以这么用就可以了,但为什么一定要这么搞,是ATL发神经吗


template <class T>
class B1
{
public:
    void SayHi()
    {
        T* pT = static_cast<T*>(this);   // HUH?? 我将在下面解释
 
        pT->PrintClassName();
    }
protected:
    void PrintClassName() { cout << "This is B1"; }
};
 
class D1 : public B1<D1>
{
    // No overridden functions at all
};
 
class D2 : public B1<D2>
{
protected:
    void PrintClassName() { cout << "This is D2"; }
};
 
main()
{
    D1 d1;
    D2 d2;
 
    d1.SayHi();    // prints "This is B1"
    d2.SayHi();    // prints "This is D2"
}

看看上面的代码,就是为了这个static_cast<T*>(this)才这么干,
简单的看一下,就是为了搞个运行时的类别绑定,比虚函数牛,还节约了虚函数表
就是这样子

好像上面说的还是废话,我最近废话特别的多了。

ATL的窗口类,都是由这个CWindow,它就是一个api的包装类,里面大量的api调用,成员只有一个
窗口句柄

而CWindowImpl类则是一个真正意义上的窗口类,它实现了上面类所没有的几个方法,比如窗口过程
CDialogImpl类是对话框类的实现
CAxDialogImpl类是ActiveX的实现

任何非对话框窗口都是从CWindowImpl 派生的,对话框可不是一个简单的窗口。

要写个新的窗口,3件事情你要做。
一个窗口类的定义,
class CMyWindow : public CWindowImpl<CMyWindow>
{
public:
    DECLARE_WND_CLASS(_T("My Window Class"))
};
这样写就可以了。
就是为了RegisterWindow用,定义一个WNDCLASSEX
是不是比MFC简单多了



一个消息映射链,
class CMyWindow : public CWindowImpl<CMyWindow>
{
public:
    DECLARE_WND_CLASS(_T("My Window Class"))
 
    BEGIN_MSG_MAP(CMyWindow)
    END_MSG_MAP()
};
这就是消息映射,展开了就是switch,更加阳春。

窗口使用的默认窗口类型
就是窗口类型,扩展类型什么的。

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()
};
这样就ok了,很透明吧,写个Trait的定义就可以了,完全就是api嘛。

下面就是要写写这个消息映射表。

class CMyWindow : public CWindowImpl<CMyWindow, CWindow, CFrameWinTraits>
{
public:
    DECLARE_WND_CLASS(_T("My Window Class"))
 
    BEGIN_MSG_MAP(CMyWindow)
        MESSAGE_HANDLER(WM_CLOSE, OnClose)
        MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
    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;
    }
};

注意最后一个参数bHandled,如果在你的消息响应处理完之后需要ATL调用默认的WindowProc()处理该消息,
你可以讲bHandled设置为FALSE。这与MFC不同, MFC是显式的调用基类的响应函数来实现的默认的消息处理的

class CMyWindow : public CWindowImpl<CMyWindow, CWindow, CFrameWinTraits>
{
public:
    DECLARE_WND_CLASS(_T("My Window Class"))
 
    BEGIN_MSG_MAP(CMyWindow)
        MESSAGE_HANDLER(WM_CLOSE, OnClose)
        MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
        COMMAND_ID_HANDLER(IDC_ABOUT, OnAbout)
    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;
    }
};

这是加了一个WM_COMMAND消息,
同样,NOTIFY_HANDLER宏也将WM_NOTIFY消息的参数展开了。

这个貌似不错,但要是想重用以前写好的消息映射怎么处理,mfc是通过将不能处理的消息
交给基类处理,ATL可没有这么干。

它更直接更直观

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()
...
};

CHAIN_MSG_MAP(CPaintBkgndBase)这个就是高级消息映射链,就是将不能处理的消息交给链中的处理,

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也是从CMessageMap派生的,这并不是必须的,
因为所有需要响应消息的类只需使用BEGIN_MSG_MAP宏就足够了,
所以你可能看到其他的一些嵌入类的例子代码,它们并不是从该基类派生的。

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

class ATL_NO_VTABLE CMessageMap
{
public:
    virtual BOOL ProcessWindowMessage(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam,
        LRESULT& lResult, DWORD dwMsgMapID) = 0;
};

这个就是上面用的类,就这样,这个源码,可以说就这样就可以映射了,不用是窗口也可以。


窗口类基本就这么简单的建立好了,又简单又透明,是不是很爽啊

接着你需要让你的程序跑起来。
一个ATL程序包含一个CComModule类型的全局变量_Module,这和MFC的程序都有一个CWinApp类型的全局变量theApp有些类似,
唯一不同的是在ATL中这个变量必须命名为 _Module。

#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;
}

这样写是必须的,没有mfc简单,但是很透明啊不是吗?
就是加个消息泵。

CWindow::rcDefault是RECT。
和调用CreateWindow() API时使用CW_USEDEFAULT指定窗口的宽度和高度一样,
ATL使用rcDefault作为窗口的最初大小。

文章转至http://www.imyaker.com/wtl/,加入了自己的理解。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值