源代码下载: http://download.csdn.net/source/3522792
WTL Overview
WTL的类可以分为以下几类:
1)窗口实现类 - CFrameWindowImpl, CMDIFrameWindowImpl …
2)控件封装类 - CButton, CListViewCtrl …
3)GDI封装类 - CDC, CMenu …
4)特殊的UI特性 - CSplitterWindow, CUpdateUI, CDialogResize, CCustomDraw …
5)基础类和宏 - CString, CRect, BEGIN_MSG_MAP_EX …
Beginning a WTL EXE
我们从零开始不通过WTL AppWizard创建一个WTL项目。
1)在Stdafx.h添加头文件和定义宏
-
-
- #define STRICT
-
- #define WIN32_LEAN_AND_MEAN
-
- #define _WTL_USE_CSTRING // 如果计划使用WTL封装的CString类,需要定义此宏。
-
-
-
-
-
-
-
- #include <atlbase.h> // base ATL classes
-
- #include <atlapp.h> // base WTL classes,包含消息处理和CAppModule的定义(派生于CComModule类)
-
- extern CAppModule _Module;
-
- #include <atlwin.h> // ATL GUI classes
-
- #include <atlframe.h> // WTL frame window classes
-
- #include <atlmisc.h> // WTL utility classes like CString
-
- #include <atlcrack.h> // WTL enhanced msg map macros
2)定义一个Frame Window,其派生于CFrameWindowImpl。并且用DECLARE_FRAME_WND_CLASS宏定义窗口类。
-
-
- class CMyWindow : public CFrameWindowImpl<CMyWindow>
-
- {
-
- public:
-
-
-
-
-
-
-
-
-
- DECLARE_FRAME_WND_CLASS(_T("First WTL window"), IDR_MAINFRAME);
-
-
-
- BEGIN_MSG_MAP(CMyWindow)
-
-
-
- CHAIN_MSG_MAP(CFrameWindowImpl<CMyWindow>)
-
- END_MSG_MAP()
-
- }
3)WinMain函数,与上一节相似:
- #include "stdafx.h"
-
- #include "MyWindow.h"
-
-
-
- CAppModule _Module;
-
-
-
- int APIENTRY WinMain ( HINSTANCE hInstance, HINSTANCE hPrevInstance,
-
- LPSTR lpCmdLine, int nCmdShow )
-
- {
-
- _Module.Init ( NULL, hInstance );
-
-
-
- CMyWindow wndMain;
-
- MSG msg;
-
-
-
-
-
- if ( NULL == wndMain.CreateEx() )
-
- return 1;
-
-
-
-
-
- wndMain.ShowWindow ( nCmdShow );
-
- wndMain.UpdateWindow();
-
-
-
-
-
- while ( GetMessage ( &msg, NULL, 0, 0 ) > 0 )
-
- {
-
- TranslateMessage ( &msg );
-
- DispatchMessage ( &msg );
-
- }
-
-
-
- _Module.Term();
-
- return msg.wParam;
-
- }
此时运行程序,将会起动一个空窗口的。
WTL Message Map Enhancements
ATL中需要对消息参数WPARAM和LPARAM进行解包的,但是WTL中增强的消息路由机制已经帮我们解决了此问题,
对于ATL3.0,必须用宏BEGIN_MSG_MAP_EX定义消息路由
对于ATL7.0/7.1,从CwindowImpl和CDialogImpl派生的类可以直接使用 宏BEGIN_MSG_MAP定义消息路由,其他类必须使用宏BEGIN_MSG_MAP_EX定义消息路由。
4)添加WM_CREATE消息处理
- class CMyWindow : public CFrameWindowImpl<CMyWindow>
-
- {
-
- public:
-
- BEGIN_MSG_MAP_EX(CMyWindow)
-
-
-
- MSG_WM_CREATE(OnCreate)
-
- CHAIN_MSG_MAP(CFrameWindowImpl<CMyWindow>)
-
- END_MSG_MAP()
-
-
-
-
-
- };
-
- MSG_WM_CREATE宏的定义如下:
-
- #define MSG_WM_CREATE(func) \
-
- if (uMsg == WM_CREATE) \
-
- { \
-
- SetMsgHandled(TRUE); \
-
- lResult = (LRESULT)func((LPCREATESTRUCT)lParam); \
-
- if(IsMsgHandled()) \
-
- return TRUE; \
-
- }
可以看出,消息参数被解包为LPCREATESTRUCT,只有一个参数,返回值为LRESULT对象。同时没有了bHandle参数,通过SetMsgHandled()代替此参数。
- class CMyWindow : public CFrameWindowImpl<CMyWindow>
-
- {
-
- public:
-
- BEGIN_MSG_MAP_EX(CMyWindow)
-
- MSG_WM_CREATE(OnCreate)
-
- CHAIN_MSG_MAP(CFrameWindowImpl<CMyWindow>)
-
- END_MSG_MAP()
-
-
-
- LRESULT OnCreate(LPCREATESTRUCT lpcs)
-
- {
-
- SetTimer ( 1, 1000 );
-
- SetMsgHandled(false);
-
- return 0;
-
- }
-
- };
CFrameWindowImpl派生于CWindow,故其有SetTimer()函数。设置每1s触发一个计时消息的计时器。同时用SetMsgHandled(false);设置其基类也能处理该消息。这是一个好的习惯。
5)添加WM_DESTORY处理,销毁计时器
MSG_WM_DESTORY宏的定义如下:
- #define MSG_WM_DESTROY(func) \
-
- if (uMsg == WM_DESTROY) \
-
- { \
-
- SetMsgHandled(TRUE); \
-
- func(); \
-
- lResult = 0; \
-
- if(IsMsgHandled()) \
-
- return TRUE; \
-
- }
其定义如下:
- class CMyWindow : public CFrameWindowImpl<CMyWindow>
-
- {
-
- public:
-
- BEGIN_MSG_MAP_EX(CMyWindow)
-
- MSG_WM_CREATE(OnCreate)
-
- MSG_WM_DESTROY(OnDestroy)
-
- CHAIN_MSG_MAP(CFrameWindowImpl<CMyWindow>)
-
- END_MSG_MAP()
-
-
-
- void OnDestroy()
-
- {
-
- KillTimer(1);
-
- SetMsgHandled(false);
-
- }
-
- };
6)添加WM_TIMER处理,每秒刷新一次窗口
- class CMyWindow : public CFrameWindowImpl<CMyWindow>
-
- {
-
- public:
-
- BEGIN_MSG_MAP_EX(CMyWindow)
-
- MSG_WM_CREATE(OnCreate)
-
- MSG_WM_DESTROY(OnDestroy)
-
- MSG_WM_TIMER(OnTimer)
-
- CHAIN_MSG_MAP(CFrameWindowImpl<CMyWindow>)
-
- END_MSG_MAP()
-
-
-
- void OnTimer ( UINT uTimerID, TIMERPROC pTimerProc )
-
- {
-
- if ( 1 != uTimerID )
-
- SetMsgHandled(false);
-
- else
-
- RedrawWindow();
-
- }
-
- };
7)添加WM_ERASEBKGND消息处理,用于刷新背景色,绘制当前时间
- class CMyWindow : public CFrameWindowImpl<CMyWindow>
-
- {
-
- public:
-
- BEGIN_MSG_MAP_EX(CMyWindow)
-
- MSG_WM_CREATE(OnCreate)
-
- MSG_WM_DESTROY(OnDestroy)
-
- MSG_WM_TIMER(OnTimer)
-
- MSG_WM_ERASEBKGND(OnEraseBkgnd)
-
- CHAIN_MSG_MAP(CFrameWindowImpl<CMyWindow>)
-
- END_MSG_MAP()
-
-
-
- LRESULT OnEraseBkgnd ( HDC hdc )
-
- {
-
- CDCHandle dc(hdc);
-
- CRect rc;
-
- SYSTEMTIME st;
-
- CString sTime;
-
-
-
-
-
- GetClientRect ( rc );
-
-
-
-
-
- GetLocalTime ( &st );
-
- sTime.Format ( _T("The time is %d:%02d:%02d"),
-
- st.wHour, st.wMinute, st.wSecond );
-
-
-
-
-
- dc.SaveDC();
-
-
-
- dc.SetBkColor ( RGB(255,153,0) );
-
- dc.SetTextColor ( RGB(0,0,0) );
-
- dc.ExtTextOut ( 0, 0, ETO_OPAQUE, rc, sTime,
-
- sTime.GetLength(), NULL );
-
-
-
-
-
- dc.RestoreDC(-1);
-
- return 1;
-
- }
-
- };
Generate Codes by AppWizard(VC8)
1) 自动生成了三个类:CMainFrame,CAboutDlg,CxxxView。
2) 自动生成了程序入口函数_tWinMain(),用于初始化COM、普通控件以及_module,然后调用一个全局的Run()。Run()里创建消息循环类CMessageLoop,并且创建窗口。
3) CMainFrame类:
- class CMainFrame : public CFrameWindowImpl<CMainFrame>,
-
- public CUpdateUI<CMainFrame>,
-
- public CMessageFilter,
-
- public CIdleHandler
-
- {
-
- public:
-
- DECLARE_FRAME_WND_CLASS(NULL, IDR_MAINFRAME)
-
-
-
- BEGIN_UPDATE_UI_MAP(CMainFrame)
-
- END_UPDATE_UI_MAP()
-
-
-
- BEGIN_MSG_MAP(CMainFrame)
-
-
-
- CHAIN_MSG_MAP(CUpdateUI<CMainFrame>)
-
- CHAIN_MSG_MAP(CFrameWindowImpl<CMainFrame>)
-
- END_MSG_MAP()
-
-
-
- BOOL PreTranslateMessage(MSG* pMsg);
-
- BOOL OnIdle();
-
-
-
- protected:
-
- CWTLClockView m_view;
-
- };
CMessageFilter是一个提供PreTranslateMessage()的混合类,CIdleHandler是一个提供OnIdle()的混合类。CMessageLoop, CIdleHandler, 和 CUpdateUI一起提供UI的更新(类似于MFC中ON_UPDATE_COMMAND_UI)。
CMainFrame::OnCreate()创建view窗口,并保存其句柄,以使主窗口大小变化时view窗口能随之变化。同时把CMainFrame对象添加到_module的消息过滤器和idle handler中。
- LRESULT CMainFrame::OnCreate(UINT , WPARAM ,
-
- LPARAM , BOOL& )
-
- {
-
- m_hWndClient = m_view.Create(m_hWnd, rcDefault, NULL, |
-
- WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS |
-
- WS_CLIPCHILDREN, WS_EX_CLIENTEDGE);
-
-
-
-
-
- CMessageLoop* pLoop = _Module.GetMessageLoop();
-
- pLoop->AddMessageFilter(this);
-
- pLoop->AddIdleHandler(this);
-
-
-
- return 0;
-
- }
CMessageLoop Internals
CMessageLoop提供程序的消息泵。不仅提供TranslateMessage/DispatchMessage,还提供消息过滤PreTranslateMessage()和空闲处理OnIdle()。下面是CMessageLoop::Run()的伪代码:
- int Run()
-
- {
-
- MSG msg;
-
-
-
- for(;;)
-
- {
-
- while ( !PeekMessage(&msg) )
-
- CallIdleHandlers();
-
-
-
- if ( 0 == GetMessage(&msg) )
-
- break;
-
-
-
- if ( !CallPreTranslateMessageFilters(&msg) )
-
- {
-
-
-
- TranslateMessage(&msg);
-
- DispatchMessage(&msg);
-
- }
-
- }
-
-
-
- return msg.wParam;
-
- }
通过在窗口对象创建时调用CMessageLoop::AddMessageFilter()和CMessageLoop::AddIdleHandler(),就可以实现消息泵了。
本消息池中不处理TranslateAccelerator() 和 IsDialogMessage()。它们在CFrameWindowImpl中处理。如果要在程序中添加非模式对话框,需要在CMainFrame:: PreTranslateMessage()中调用IsDialogMessage()。
CFrameWindowImpl Internals
CFrameWindowImpl和其基类CFrameWindowImplBase提供了大量的特性,如工具条、状态条、tooltips,toolButton等。
1)WM_SIZE消息。CFrameWindowImplBase的成员m_hWndClient存储Frame的view视图句柄。因此:
- LRESULT OnSize(UINT , WPARAM wParam, LPARAM , BOOL& bHandled)
-
- {
-
-
-
- if(wParam != SIZE_MINIMIZED)
-
- {
-
- T* pT = static_cast<T*>(this);
-
- pT->UpdateLayout();
-
- }
-
-
-
- bHandled = FALSE;
-
- return 1;
-
- }
-
- void UpdateLayout(BOOL bResizeBars = TRUE)
-
- {
-
- RECT rect;
-
- GetClientRect(&rect);
-
-
-
-
-
- UpdateBarsPosition(rect, bResizeBars);
-
-
-
-
-
- if(m_hWndClient != NULL)
-
- ::SetWindowPos(m_hWndClient, NULL, rect.left, rect.top,
-
- rect.right - rect.left, rect.bottom - rect.top,
-
- SWP_NOZORDER | SWP_NOACTIVATE);
-
- }
Back to the Clock Program
- class CWTLClockView : public CWindowImpl<CWTLClockView>
-
- {
-
- public:
-
- DECLARE_WND_CLASS(NULL)
-
-
-
- BOOL PreTranslateMessage(MSG* pMsg);
-
-
-
- BEGIN_MSG_MAP_EX(CWTLClockView)
-
- MESSAGE_HANDLER(WM_PAINT, OnPaint)
-
- MSG_WM_CREATE(OnCreate)
-
- MSG_WM_DESTROY(OnDestroy)
-
- MSG_WM_TIMER(OnTimer)
-
- MSG_WM_ERASEBKGND(OnEraseBkgnd)
-
- END_MSG_MAP()
-
- };
UI Updating
处理UI的更新需要三个类的结合:CMessageLoop,CIdleHandler,CUpdateUI。CUpdateUIBase中存储5个常量对应于5中控件的更新。
- menu bar items: UPDUI_MENUBAR
- popup menu items: UPDUI_MENUPOPUP
- toolbar buttons: UPDUI_TOOLBAR
- status bar panes: UPDUI_STATUSBAR
- child windows: UPDUI_CHILDWINDOW
CUpdateUI可以设置控件的enabled state, checked state, and text of items等。
关于UI更新,我们有四件事情要做:
1) 从CUpdateUI和CIdleHandler派生Frame窗口类
2) 从CFrameWindow链接消息到CUpdateUI
3) 添加Frame窗口类到_module的Idle handles的列表中
4) 填充Frame窗口下的UPDATE_UI_MAP宏
New menu items to control the clock
添加两个菜单项 ID_CLOCK_START, ID_CLOCK_STOP,用来控制时钟。
- class CMainFrame : public ...
-
- {
-
- public:
-
-
-
- BEGIN_UPDATE_UI_MAP(CMainFrame)
-
- UPDATE_ELEMENT(ID_CLOCK_START, UPDUI_MENUPOPUP)
-
- UPDATE_ELEMENT(ID_CLOCK_STOP, UPDUI_MENUPOPUP)
-
- END_UPDATE_UI_MAP()
-
-
-
- };
Calling UIEnable()
通过上面的方法,来控制UI的可操作性。
- LRESULT CMainFrame::OnCreate(UINT , WPARAM ,
-
- LPARAM , BOOL& )
-
- {
-
- m_hWndClient = m_view.Create(...);
-
-
-
-
-
-
-
-
-
-
-
- UIEnable (ID_CLOCK_START, false );
-
- UIEnable (ID_CLOCK_STOP, true );
-
-
-
- return 0;
-
- }
参考原文
http://download.csdn.net/source/3479570
转自:http://blog.csdn.net/wcyoot/article/details/6644863