WTL入门(2)-- WTL基础

 源代码下载: 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添加头文件和定义宏

// stdafx.h

#define STRICT

#define WIN32_LEAN_AND_MEAN

#define _WTL_USE_CSTRING   // 如果计划使用WTL封装的CString类,需要定义此宏。

// CString是在atlmisc.h中定义的,但是在其他的头文件中可能会用到此类,尤其在atlmisc.h之前被包含的头文件如AtlApp.h

// 因此,通过这种方法可以预声明一个CString类,以告诉其他头文件CString是个什么东东

 

#include <atlbase.h>       // base ATL classes

#include <atlapp.h>        // base WTL classes,包含消息处理和CAppModule的定义(派生于CComModule类)

extern CAppModule _Module; // WTL version of CComModule,它包含我们所需要的空闲进程和UI更新等特性

#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宏定义窗口类。

// MyWindow.h:

class CMyWindow : public CFrameWindowImpl<CMyWindow>

{

public:

     // 两个参数,前者表示窗口名字,如果为NULL,WTL将自动生成一个

     // 后者表示窗口的资源ID。

// WTL将会根据此ID搜索图标、菜单、快捷键表等资源,并在窗口创建时加载它们。

// 同样也通过此ID搜索一个字符串用于窗口的Title

     DECLARE_FRAME_WND_CLASS(_T("First WTL window"), IDR_MAINFRAME);

 

     BEGIN_MSG_MAP(CMyWindow)

         // 用这种方法将消息链到其基类中(值得注意的是WM_SIZE和WM_DESTORY)

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;

 

     // Create the main window

     if ( NULL == wndMain.CreateEx() )

         return 1;       // Window creation failed

 

     // Show the window

     wndMain.ShowWindow ( nCmdShow );

     wndMain.UpdateWindow();

 

     // Standard Win32 message loop

     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)

    // 用这种方法声明WM_CREATE消息的处理

        MSG_WM_CREATE(OnCreate)

        CHAIN_MSG_MAP(CFrameWindowImpl<CMyWindow>)

    END_MSG_MAP()

 

    // OnCreate(...) ?

};

         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;

 

        // Get our window's client area.

        GetClientRect ( rc );

 

        // Build the string to show in the window.

        GetLocalTime ( &st );

        sTime.Format ( _T("The time is %d:%02d:%02d"), 

                       st.wHour, st.wMinute, st.wSecond );

 

        // Set up the DC and draw the text.

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

 

        // Restore the DC.

        dc.RestoreDC(-1);

        return 1;    // We erased the background (ExtTextOut did it)

    }

};

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 /*uMsg*/, WPARAM /*wParam*/, 

                             LPARAM /*lParam*/, BOOL& /*bHandled*/)

{

    m_hWndClient = m_view.Create(m_hWnd, rcDefault, NULL, |

                                 WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS |

                                   WS_CLIPCHILDREN, WS_EX_CLIENTEDGE);

 

    // register object for message filtering and idle updates

    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;    // WM_QUIT retrieved from the queue

 

        if ( !CallPreTranslateMessageFilters(&msg) )

            {

            // if we get here, message was not filtered out

            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 /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled)

{

    // 检查是否最小化,如果不是,调用UpdateLayout()

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

 

    // position bars and offset their dimensions

    UpdateBarsPosition(rect, bResizeBars);

 

    // resize client window

    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 /*uMsg*/, WPARAM /*wParam*/, 

                             LPARAM /*lParam*/, BOOL& /*bHandled*/)

{

    m_hWndClient = m_view.Create(...);

 

    // register object for message filtering and idle updates

    // [omitted for clarity]

 

    // Set the initial state of the Clock menu items:

    UIEnable (ID_CLOCK_START, false );

    UIEnable (ID_CLOCK_STOP, true );

 

    return 0;

}

参考原文

http://download.csdn.net/source/3479570

你可能感兴趣的:(UI,timer,Module,null,Class,menu)