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/,加入了自己的理解。
ATL GUI (三)
最新推荐文章于 2018-09-09 16:46:16 发布