代码改变世界

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

这个类东西太多,写点是点

 

参考:http://www.vckbase.com/document/viewdoc/?id=1119