从今天开始我要进去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/,加入了自己的理解。
ATL GUI (一)
最新推荐文章于 2016-10-01 13:47:00 发布