ATL GUI (三)

来源:互联网 发布:安广网络怎么样 编辑:程序博客网 时间:2024/05/16 19:48

ATL GUI
开始WTL了

atlapp.h 是你的工程中第一个包含的头文件,
这个文件内定义了有关消息处理的类和CAppModule,CAppModule是从CComModule派生的类。

接下来定义框架窗口。
我们的SDI窗口是从CFrameWindowImpl派生的,
在定义窗口类时使用 DECLARE_FRAME_WND_CLASS代替前面使用的DECLARE_WND_CLASS。
下面时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()
};

DECLARE_FRAME_WND_CLASS有两个参数,
窗口类名(类名可以是NULL,ATL会替你生成一个类名)和资源ID,
创建窗口时WTL用这个ID装载图标,菜单和加速键表。
我们还要象CFrameWindowImpl中的消息处理(例如WM_SIZE和WM_DESTROY消息)
那样将消息链入窗口的消息中。

// 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()函数的参数使用了常用的默认值,
所以我们不需要特别指定任何参数。正如前面介绍的,CFrameWindowImpl会处理资源的装载,
你只需要使用IDR_MAINFRAME作为ID定义你的资源就行了


ATL最大的问题就是消息处理函数的签名问题,
MSG_WM_CREATE,MSG_WM_DESTROY等宏可以解决这个问题,直接使用F12看看它的定义就可以解决函数的签名问题。

WTL的向导一般会生成三个类CMainFrame, CAboutDlg, 和CWTLClockView

还有一个_tWinMain()函数,它先初始化COM环境,公用控件和_Module,
然后调用全局函数Run()。Run()函数创建主窗口并开始消息循环,
Run()调用 CMessageLoop::Run(),消息泵实际上是位于CMessageLoop::Run()内

CWTLClockView是我们的程序的视图类,它的作用和MFC的视图类一样,没有标题栏,覆盖整个主窗口的客户区。
CWTLClockView类有一个PreTranslateMessage()函数,也和MFC中的同名函数作用相同,还有一个WM_PAINT的消息响应函数。
这两个函数都没有什么特别之处,只是我们会填写OnPaint()函数来显示时间。

class CMainFrame : public CFrameWindowImpl<CMainFrame>,
                   public CUpdateUI<CMainFrame>,
                   public CMessageFilter,
                   public CIdleHandler
{
public:
    DECLARE_FRAME_WND_CLASS(NULL, IDR_MAINFRAME)

    CWTLClockView m_view;

    virtual BOOL PreTranslateMessage(MSG* pMsg);
    virtual BOOL OnIdle();

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

CMessageFilter是一个嵌入类,它提供PreTranslateMessage()函数,CIdleHandler也是一个嵌入类,
它提供了OnIdle()函数。CMessageLoop, CIdleHandler 和 CUpdateUI三个类互相协同完成界面元素的状态更新(UI update),
就像MFC中的ON_UPDATE_COMMAND_UI宏一样。

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

CMainFrame::OnCreate()中创建了视图窗口并保存这个窗口的句柄,当主窗口改变大小时视图窗口的大小也会随之改变。
OnCreate()函数还将CMainFrame对象添加到由CAppModule维持的消息过滤器队列和空闲处理队列

CMessageLoop为我们的应用程序提供一个消息泵,除了一个标准的DispatchMessage/TranslateMessage 循环外,
它还通过调用PreTranslateMessage()函数实现了消息过滤机制,通过调用OnIdle()实现了空闲处理功能。下面是Run ()函数的伪代码:

int Run()
{
MSG msg;
 
    for(;;)
        {
        while ( !PeekMessage(&msg) )
            DoIdleProcessing();
 
        if ( 0 == GetMessage(&msg) )
            break;    // WM_QUIT retrieved from the queue
 
        if ( !PreTranslateMessage(&msg) )
            {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
            }
        }
 
    return msg.wParam;
}

CFrameWindowImpl类处理了WM_SIZE消息
LRESULT OnSize(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled)
{
    if(wParam != SIZE_MINIMIZED)
    {
        T* pT = static_cast<T*>(this);
        pT->UpdateLayout();
    }
 
    bHandled = FALSE;
    return 1;
}

它首先检查窗口是否最小化,如果不是就调用UpdateLayout(),下面是UpdateLayout():
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);
}

界面的东西还有很多,不急,下面一篇就来说菜单和状态栏
文章转至http://www.imyaker.com/wtl/,加入了自己的理解。

原创粉丝点击