WTL for MFC Programmers 学习笔记(二) Part II WTL GUI Base Classes

Party II WTL GUI Base Classes

WTL Overview

       WTL类主要分成一下5类:

1.       Frame窗口实现: CFrameWindowImpl, CMDIFrameWindowImple

2.       空间封装:CButton, CListViewCtrl

3.       GDI封装:CDC, CMenu

4.       特殊UI特征:CSplitterWindow, CUpdateUI, CDialogResize, CCustomDraw

5.       工具类和宏:CString CRect BEGIN_MSG_MAP_EX

其中多数是独立类,但也包括嵌入类如: CDialogResize

 

Beginning a WTL EXE

// stdafx.h

#define STRICT

#define WIN32_LEAN_AND_MEAN

#define _WTL_USE_CSTRING

 

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

#include <atlapp.h>        // base WTL classes

extern CAppModule _Module; // WTL version of CComModule

#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

       Atlapp.h是第一个引入的WTL头文件,其中包含 了消息处理类、从CComModule继承来的CAppModule类。如果要用atlmisc.h中的CString类的话,需要定义_WTL_USE_CSTRING宏,在atlapp.h引入前定义,保证其中能使用CString

       CAppModule拥有有关空闲处理及UI更新的功能。

 

// MyWindow.h:

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

};

       Frame声明宏需要两个参数,一个窗口类名称、一个资源ID。通过资源IDWTL可在创建窗体的时候加载图标、菜单、快捷键表,并可查找一个字符串作为窗口标题。

// main.cpp:

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

}

       CFrameWindowImpl有一个CreateEx()方法,该方法有大部分普通默认值,所以不需要参数;它会处理资源加载,我们只需要创建需要的资源就行。

 

WTL Message Map Enhancements

       消息处理中复杂的一点是WPARAM/LPARAM需要自己解析,ATL也未做改进,WTL在这方面有了提高。

       WTL增强消息映射宏在atlcrack.h中,在VC6VC7中使用稍有不同:

       对于ATL3.0, 使用增强消息处理的话需要用BEGIN_MSG_MAP_EX

       对于ATL7.0/7.1,对于CWindowImpl/CDialogImpl的继承子类需使用BEGIN_MSG_MAP,对于非CWindowImpl/CDialogImpl继承子类需使用BEGIN_MSG_MAP_EX

 

// MyWindow.h, VC6 only:

class CMyWindow : public CFrameWindowImpl<CMyWindow>

{

public:

    BEGIN_MSG_MAP_EX(CMyWindow)

        CHAIN_MSG_MAP(CFrameWindowImpl<CMyWindow>)

    END_MSG_MAP()

};

       记住VC7中对于窗口子类的消息映射不需要_EX,就可以使用增强消息映射。

 

       WTL消息处理名称: MSG_ + 消息名称。 如: MSG_WM_CREATE,这个宏只需要消息处理函数名作为参数。

Class CMyWindwo : 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_EX()

 

       LRESULT OnCreate(LPCREATESTRUCT lpcs)

{

              SetTimer(1, 1000);

              SetMsgHandled(false);

              Return 0;

}

Void OnDestroy()

{

       KillTimer(1);

       SetMsgHandled(false);

}

Void OnTimer(UNIT uTimerID, TIMERPROC pTimerProc)

{

       If( 1 != uTimerID)

              SetMsgHnadled(false);

       Else

              RedrawWindow();

}

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 thewindow

       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

}

};

       WTL的消息处理函数和MFC中的相似,都对应着固定原型:具有参数和返回值。由于没有向导生成处理函数,所以需要我们自己找到函数原型,通过“将光标移至消息宏上,按F12”就可以转到宏定义处。 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类型值。这里的函数没有像ATL宏中的bHandled参数。通过SetMsgHandled()方法修改该参数。

       OnCreate中设置SetMsgHandledfalse)让父类处理该消息,尽管父类不处理该消息,都这么做,就不需要记住哪些父类是处理该消息的。

       如果要添加WM_COMMAND类型的消息映射,采用COMMAND_ID_HANDLER_EX宏。

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()方法(感觉像interface,CIdleHandler是另一个提供OnIdle()方法的嵌入类。CMessageLoopCIdleHandlerCUpdateUI一起实现了MFCON_UPDATE_COMMAND_UI的功能。

       OnCreate创建并保存视图的句柄,这样Frame窗体改变大小的时候视图大小也随着改变。同时将将本Frame窗体对象加入消息过滤和空闲处理列表。

      

CMessageLoop Internals

       CMessageLoop为应用程序提供了消息泵功能。除了标准的TranslateMessage/DispatchMessage循环歪,通过PreTranslateMessage()提供了消息过滤功能以及通过OnIdle()提供了空闲处理功能。其机制如:

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;

}

       窗体通过将自身注册到ModuleCMessageLoop中,这样消息泵就知道该调用那个窗体的PreTranslateMessage()方法和OnIdle()方法。

       注意:在消息循环中没有对TranslateAccelerator()IsDialogMessage()的调用。CFrameWindowImple处理了TranslateAccelerator方法调用,但是如果在程序中使用了无模式对话框,就需要自己在CMainFrame::PreTranslateMessage()方法中判断IsDialogMessage()

 

CFrameWindowImpl Internals

       CFrameWindowImple及其基类CFrameWindowImplBase提供了原来在MFC Frame窗体中很多特征:工具栏、rebars、状态栏工具提示、菜单项提示。m_hWndClientCFrameWindowImplBase中视图窗口的句柄。

       看一下CFrameWindowImpleWM_SIZE的处理:

LRESULT OnSize(UNIT /*UmSG*/, WPARAM wParam, LPARAM /*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);

      

       // position bars and offset theirdimensions

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

};

       注意:当使用BEGIN_MSG_MAP_EX宏(即使用增强消息映射时),消息映射宏也需要改成WTL版本。

 

UI Updating

       UI空闲时间更新由:CMessageLoop对象、CIdleHandlerCUpdateUIUPDATE_UI_MAP宏一起完成。CUpdateUI可以操作5种不同类型的元素,每种类型对应CUpdateUIBase中常量:

1.       UPDUI_MENUBAR 菜单项

2.       UPDUI_MENUPOPUP 弹出菜单

3.       UPDUI_TOOBAR 工具栏

4.       UPDUI_STATUSBAR状态栏

5.       UPDUI_CHILDWINDOW 子窗口

可以设置enabled状态、checked状态和文本。

要使用UI更新,需要做四件事:

1.       窗体从CUpdateUICIdleHandler继承

2.       消息链入CUpdateUI

3.       将窗体对象添加到Module的空闲处理列表

4.       添加UPDATE_UI_MAP映射

 

New menu items to control the clock

       增加两个时钟控制菜单:IDC_START/IDC_STOP

Class CMainFrame : public …

{

Public:

       // …

       BEGIN_UPDATE_UI_MAP(CMainFrame)

              UPDATE_ELEMENT(IDC_START, UPDI_MENUPOPUP)

              UPDATE_ELEMENT(IDC_STOP, UPDT_MENUPOPUP)

       END_UPDATE_UI_MAP()

       // …

};

       这样就可以通过CUpdteUI::UIEnable()通过传菜单idbool值来改变菜单enabled状态。

 

Calling UIEnable()

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 ( IDC_START, false );

    UIEnable ( IDC_STOP, true );

 

    return 0;

}

       CMainFrame需要处理菜单消息,改变菜单状态(因为菜单是主窗体的),然后调用视图作出响应动作。

class CMainFrame : public ...

{

public:

    BEGIN_MSG_MAP_EX(CMainFrame)

        // ...

        COMMAND_ID_HANDLER_EX(IDC_START, OnStart)

        COMMAND_ID_HANDLER_EX(IDC_STOP, OnStop)

    END_MSG_MAP()

 

    // ...

    void OnStart(UINT uCode, int nID, HWND hwndCtrl);

    void OnStop(UINT uCode, int nID, HWND hwndCtrl);

};

 

void CMainFrame::OnStart(UINT uCode, int nID, HWND hwndCtrl)

{

    // Enable Stop and disable Start

    UIEnable ( IDC_START, false );

    UIEnable ( IDC_STOP, true );

 

    // Tell the view to start its clock.

    m_view.StartClock();

}

 

void CMainFrame::OnStop(UINT uCode, int nID, HWND hwndCtrl)

{

    // Enable Start and disable Stop

    UIEnable ( IDC_START, true );

    UIEnable ( IDC_STOP, false );

 

    // Tell the view to stop its clock.

    m_view.StopClock();

}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值