ATL Windows窗体支持(1)
2011-07-14 21:33 Clingingboy 阅读(2748) 评论(0) 编辑 收藏 举报
一.原始Win32窗体
#include "stdafx.h" // Includes windows.h and tchar.h
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
// Entry point
int APIENTRY _tWinMain(HINSTANCE hinst,
HINSTANCE /*hinstPrev*/,
LPTSTR pszCmdLine,
int nCmdShow) {
// Register the main window class
LPCTSTR pszMainWndClass = __T("WindowsApp");
WNDCLASSEX wc = { sizeof(WNDCLASSEX) };
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.hInstance = hinst;
wc.hIcon = LoadIcon(0, IDI_APPLICATION);
wc.hCursor = LoadCursor(0, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wc.lpszClassName = pszMainWndClass;
wc.lpfnWndProc = WndProc;
if( !RegisterClassEx(&wc) ) return -1;
// Create the main window
HWND hwnd = CreateWindowEx(WS_EX_CLIENTEDGE,
pszMainWndClass,
__T("Windows Application"),
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0,
CW_USEDEFAULT, 0,
0, 0, hinst, 0);
if( !hwnd ) return -1;
// Show the main window
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
// Main message loop
MSG msg;
while( GetMessage(&msg, 0, 0, 0) ) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
// Windows procedure
LRESULT CALLBACK WndProc(HWND hwnd, UINT nMsg, WPARAM wparam,
LPARAM lparam) {
switch( nMsg ) {
// Message handlers for messages we're interested in
case WM_PAINT: {
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
RECT rect; GetClientRect(hwnd, &rect);
DrawText(hdc, __T("Hello, Windows"), -1, &rect,
DT_CENTER | DT_VCENTER | DT_SINGLELINE);
EndPaint(hwnd, &ps);
}
break;
// Post the quit message when main window is destroyed
case WM_DESTROY:
PostQuitMessage(0);
break;
// Let Windows handle messages we don't want
default:
return DefWindowProc(hwnd, nMsg, wparam, lparam);
break;
}
return 0;
}
二.CWindow
首先封装这一部分代码
// Create the main window
HWND hwnd = CreateWindowEx(WS_EX_CLIENTEDGE,
pszMainWndClass,
__T("Windows Application"),
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0,
CW_USEDEFAULT, 0,
0, 0, hinst, 0);
if( !hwnd ) return -1;
// Show the main window
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
用CWindow替换此部分代码
CWindow wnd;
wnd.Create(pszMainWndClass, 0, CWindow::rcDefault,
__T("Windows Application"), WS_OVERLAPPEDWINDOW,
WS_EX_CLIENTEDGE );
if (!wnd) {
return FALSE;
}
wnd.CenterWindow( );
wnd.ShowWindow( nCmdShow );
wnd.UpdateWindow( );
即将操作封装在一个类中,其他不变
这里似乎看不到有多少的便利性,CWindow还提供了一些常用操作Windows的方法,内部存有一个HWND句柄
三.CWindowImpl
3.1迁移消息循环
class CMainWindow : public CWindowImpl<CMainWindow> {
BEGIN_MSG_MAP( CMainWindow )
MESSAGE_HANDLER( WM_PAINT, OnPaint )
MESSAGE_HANDLER( WM_DESTROY, OnDestroy )
END_MSG_MAP()
LRESULT OnPaint( UINT, WPARAM, LPARAM, BOOL& ){
PAINTSTRUCT ps;
HDC hdc = BeginPaint(&ps);
RECT rect; GetClientRect(&rect);
DrawText(hdc, __T("Hello, Windows"), -1, &rect,
DT_CENTER | DT_VCENTER | DT_SINGLELINE);
EndPaint(&ps);
return 0;
}
LRESULT OnDestroy( UINT, WPARAM, LPARAM, BOOL& ){
PostQuitMessage( 0 );
return 0;
}
};
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
// Entry point
int APIENTRY _tWinMain(HINSTANCE hinst,
HINSTANCE /*hinstPrev*/,
LPTSTR pszCmdLine,
int nCmdShow) {
CMainWindow wnd;
wnd.Create(NULL, CMainWindow::rcDefault,
__T("Windows Application"), WS_OVERLAPPEDWINDOW|WS_VISIBLE);
// Main message loop
MSG msg;
while( GetMessage(&msg, 0, 0, 0) ) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
现在的代码明显简化
CWindowImpl的消息循环类似MFC,BEGIN_MSG_MAP,MESSAGE_HANDLER,END_MSG_MAP
展开后代码类似如下
// BEGIN_MSG_MAP(CMainWindow)
BOOL ProcessWindowMessage(HWND hWnd, UINT uMsg, WPARAM wParam,
LPARAM lParam, LRESULT& lResult,
DWORD dwMsgMapID = 0) {
BOOL bHandled = TRUE;
switch (dwMsgMapID) {
case 0:
// MESSAGE_HANDLER(WM_PAINT, OnPaint)
if(uMsg == WM_PAINT) {
bHandled = TRUE;
lResult = OnPaint(uMsg, wParam, lParam, bHandled);
if (bHandled) return TRUE;
}
// END_MSG_MAP()
break;
default:
ATLTRACE2(atlTraceWindowing, 0,
_T("Invalid message map ID (%i)\n"),
dwMsgMapID);
ATLASSERT(FALSE);
break;
}
return FALSE;
}
3.2命令处理
如下COMMAND_HANDLER,即WM_COMMAND 的处理
class CMainWindow : public CWindowImpl<CMainWindow> {
public:
BEGIN_MSG_MAP(CMainWindow)
MESSAGE_HANDLER(WM_PAINT, OnPaint)
COMMAND_HANDLER(ID_FILE_EXIT, 0, OnFileExit)
COMMAND_HANDLER(ID_HELP_ABOUT, 0, OnHelpAbout)
END_MSG_MAP()
...
LRESULT OnFileExit(WORD wNotifyCode, WORD wID, HWND hWndCtl,
BOOL& bHandled);
LRESULT OnHelpAbout(WORD wNotifyCode, WORD wID, HWND hWndCtl,
BOOL& bHandled);
};
3.3消息链
在窗体继承关系时,需要使用消息链才可以使子类使用父级消息,用CHAIN_MSG_MAP指向父类
template <typename Deriving>
class CFileHandler {
public:
// Message map in base class
BEGIN_MSG_MAP(CMainWindow)
COMMAND_ID_HANDLER(ID_FILE_NEW, OnFileNew)
COMMAND_ID_HANDLER(ID_FILE_OPEN, OnFileOpen)
COMMAND_ID_HANDLER(ID_FILE_SAVE, OnFileSave)
COMMAND_ID_HANDLER(ID_FILE_SAVE_AS, OnFileSaveAs)
COMMAND_ID_HANDLER(ID_FILE_EXIT, OnFileExit)
END_MSG_MAP()
LRESULT OnFileNew(WORD, WORD, HWND, BOOL&);
LRESULT OnFileOpen(WORD, WORD, HWND, BOOL&);
LRESULT OnFileSave(WORD, WORD, HWND, BOOL&);
LRESULT OnFileSaveAs(WORD, WORD, HWND, BOOL&);
LRESULT OnFileExit(WORD, WORD, HWND, BOOL&);
};
class CMainWindow :
public CWindowImpl<CMainWindow, CWindow, CMainWinTraits>,
public CFileHandler<CMainWindow>
{
public:
BEGIN_MSG_MAP(CMainWindow)
MESSAGE_HANDLER(WM_PAINT, OnPaint)
COMMAND_ID_HANDLER(ID_HELP_ABOUT, OnHelpAbout)
// Chain to a base class
CHAIN_MSG_MAP(CFileHandler<CMainWindow>)
END_MSG_MAP()
...
};
3.4分段消息链
即不想全部消息继承,可以选段CHAIN_MSG_MAP_ALT
// in class CBase:
BEGIN_MSG_MAP( CBase )
MESSAGE_HANDLER( WM_CREATE, OnCreate1 )
MESSAGE_HANDLER( WM_PAINT, OnPaint1 )
ALT_MSG_MAP( 100 )
MESSAGE_HANDLER( WM_CREATE, OnCreate2 )
MESSAGE_HANDLER( WM_PAINT, OnPaint2 )
ALT_MSG_MAP( 101)
MESSAGE_HANDLER( WM_CREATE, OnCreate3 )
MESSAGE_HANDLER( WM_PAINT, OnPaint3 )
END_MSG_MAP()
class CDerived: public CBase {
BEGIN_MSG_MAP( CDerived )
CHAIN_MSG_MAP_ALT( CBase, 100 )
END_MSG_MAP()
...
100,将会执行OnCreate2 和OnPaint2
3.5窗体样式
typedef CWinTraits<WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN
|WS_CLIPSIBLINGS, 0> CControlWinTraits;
class CMainWindow : public CWindowImpl<CMainWindow,CWindow,CControlWinTraits> {
CControlWinTraits是默认的窗体样式,当不指定样式时,则用默认的样式
CMainWindow wnd;
wnd.Create(NULL, CMainWindow::rcDefault,
__T("Windows Application"), WS_OVERLAPPEDWINDOW|WS_VISIBLE);
也可以自定义CWinTraits,有2种方式
(1)
typedef
CWinTraits<WS_OVERLAPPEDWINDOW|WS_VISIBLE,
0>
CChildWinTraits;
class CMainWindow : public CWindowImpl<CMainWindow,CWindow,CChildWinTraits> {
(2)
class CMainWindow : public CWindowImpl<CMainWindow,CWindow,CWinTraits<WS_OVERLAPPEDWINDOW|WS_VISIBLE,0>> {
这样在创建窗体,可以不指定窗体样式参数
3.6修改窗体类名
DECLARE_WND_CLASS("my window class");
或者
DECLARE_WND_CLASS_EX(
"my window class", // class name
CS_HREDRAW|CS_VREDRAW, // class style
COLOR_WINDOW // background color
);
参数越多则越灵活
#define DECLARE_WND_CLASS(WndClassName) \
static ATL::CWndClassInfo& GetWndClassInfo() \
{ \
static ATL::CWndClassInfo wc = \
{ \
{ sizeof(WNDCLASSEX), CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS, StartWindowProc, \
0, 0, NULL, NULL, NULL, (HBRUSH)(COLOR_WINDOW + 1), NULL, WndClassName, NULL }, \
NULL, NULL, IDC_ARROW, TRUE, 0, _T("") \
}; \
return wc; \
}
#define DECLARE_WND_CLASS_EX(WndClassName, style, bkgnd) \
static ATL::CWndClassInfo& GetWndClassInfo() \
{ \
static ATL::CWndClassInfo wc = \
{ \
{ sizeof(WNDCLASSEX), style, StartWindowProc, \
0, 0, NULL, NULL, NULL, (HBRUSH)(bkgnd + 1), NULL, WndClassName, NULL }, \
NULL, NULL, IDC_ARROW, TRUE, 0, _T("") \
}; \
return wc; \
}
也可以直接通过获取GetWndClassInfo方法来直接修改,这样最灵活,值得注意的是光标是特殊处理
CMainWindow( ) {
CWndClassInfo& wci = GetWndClassInfo();
if( !wci.m_atom ) {
wci.m_wc.lpszMenuName = MAKEINTRESOURCE(IDC_ATLHELLOWIN);
wci.m_wc.hIcon = LoadIcon(
_AtlBaseModule.GetResourceInstance(),
MAKEINTRESOURCE(IDI_ATLHELLOWIN));
wci.m_wc.hIconSm = LoadIcon(
_AtlBaseModule.GetResourceInstance(),
MAKEINTRESOURCE(IDI_SMALL));
wci.m_wc.hbrBackground = CreateHatchBrush(HS_DIAGCROSS,
RGB(0, 0, 255));
}
}
3.7窗体超类化
就是扩展内置控件功能,只不过继承的不是Button,CheckBox而是CWindowImpl,使用DECLARE_WND_SUPERCLASS宏来标记,若指定Edit,则呈现的将是一个TextBox
DECLARE_WND_SUPERCLASS( _T("BeepButton"), _T("Edit") )
指定Button则显示一个按钮
class CBeepButton: public CWindowImpl< CBeepButton >
{
public:
DECLARE_WND_SUPERCLASS( _T("BeepButton"), _T("Button") )
BEGIN_MSG_MAP( CBeepButton )
MESSAGE_HANDLER( WM_LBUTTONDOWN, OnLButtonDown )
END_MSG_MAP()
LRESULT OnLButtonDown( UINT, WPARAM, LPARAM, BOOL& bHandled )
{
MessageBeep( MB_ICONASTERISK );
bHandled = FALSE; // alternatively: DefWindowProc()
return 0;
}
}; // CBeepButton
const int ID_BUTTON1 = 101;
const int ID_BUTTON2 = 102;
class CMyWindow: public CWindowImpl< CMyWindow, CWindow,
CWinTraits<WS_OVERLAPPEDWINDOW|WS_VISIBLE> >
{
CBeepButton b1, b2;
BEGIN_MSG_MAP( CMyWindow )
MESSAGE_HANDLER( WM_CREATE, OnCreate )
COMMAND_CODE_HANDLER( BN_CLICKED, OnClick )
END_MSG_MAP()
LRESULT OnClick(WORD wNotifyCode, WORD wID, HWND hWndCtl,
BOOL& bHandled)
{
ATLTRACE( "Control %d clicked\n", wID );
return 0;
}
LRESULT OnCreate( UINT, WPARAM, LPARAM, BOOL& )
{
RECT r1 = { 10, 10, 250, 80 };
b1.Create(*this, r1, __T("beep1"), WS_CHILD|WS_VISIBLE, 0, ID_BUTTON1);
RECT r2 = { 10, 110, 250, 180 };
b2.Create(*this, r2, __T("beep2"), WS_CHILD|WS_VISIBLE, 0, ID_BUTTON2);
return 0;
}
}; // CMyWindow
// Entry point
int APIENTRY _tWinMain(HINSTANCE hinst,
HINSTANCE /*hinstPrev*/,
LPTSTR pszCmdLine,
int nCmdShow) {
CMyWindow wnd;
wnd.Create(NULL, CMyWindow::rcDefault,
__T("Windows Application"));
// Main message loop
MSG msg;
while( GetMessage(&msg, 0, 0, 0) ) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
3.8窗体子类化
这种方式更灵活,更易扩展控件行为
class CNoNumEdit: public CWindowImpl< CNoNumEdit >
{
BEGIN_MSG_MAP( CNoNumEdit )
MESSAGE_HANDLER( WM_CHAR, OnChar )
END_MSG_MAP()
LRESULT OnChar( UINT, WPARAM wParam, LPARAM, BOOL& bHandled )
{
TCHAR ch = wParam;
if( _T(''0'') <= ch && ch <= _T(''9'') )
MessageBeep( 0 );
else
bHandled = FALSE;
return 0;
}
};
const int ID_BUTTON1 = 101;
class CMyWindow: public CWindowImpl< CMyWindow, CWindow,
CWinTraits<WS_OVERLAPPEDWINDOW|WS_VISIBLE> >
{
CNoNumEdit ed;
BEGIN_MSG_MAP( CMyWindow )
MESSAGE_HANDLER( WM_CREATE, OnCreate )
END_MSG_MAP()
LRESULT OnCreate( UINT, WPARAM, LPARAM, BOOL& )
{
ed.SubclassWindow( GetDlgItem( ID_BUTTON1 ) );
return 0;
}
};
这个类东西太多,写点是点