ダブルバッファ(ダブルバッファリング)とは、あらかじめ作業用バッファ(メモリデバイスコンテキスト)にすべて描画し、
次に作業用バッファの内容を可視化することで、描画の際のちらつきを防止する手法です。
WTLはダブルバッファウィンドウを作成するためにCDoubleBufferWindowImplというクラステンプレートを用意しています。
これはATLのCWindowImplクラステンプレートのように基底クラスとしてのみ使用し、
テンプレート引数の種類もCWindowImplと同じです。
以下に示すのは、CDoubleBufferWindowImplをベースとしたビューウィンドウを作成する例です。
プロジェクトにView.hというヘッダファイルを追加し、そこにCImageViewというクラスを定義します。
ビューウィンドウにビットマップファイルを描画し、その中央に「Double Buffer」という文字列を描画します。
// View.h
#pragma once
class CImageView : public CDoubleBufferWindowImpl<CImageView>
{
public:
CBitmap m_bitmap;
BOOL PreTranslateMessage(MSG* pMsg){
return FALSE;
}
BEGIN_MSG_MAP(CImageView)
MSG_WM_CREATE(OnCreate)
CHAIN_MSG_MAP(CDoubleBufferWindowImpl<CImageView>)
END_MSG_MAP()
int OnCreate(LPCREATESTRUCT lpCreateStruct){
LRESULT lRet = DefWindowProc();
// ビットマップファイル読み込み
TCHAR szImage[_MAX_PATH];
::GetModuleFileName(NULL, szImage, sizeof(szImage) / sizeof(TCHAR));
::StringCbCopy(::_tcsrchr(szImage, _T('\\')) + 1,
sizeof(szImage), _T("sample.bmp"));
m_bitmap = ::SHLoadImageFile(szImage);
return lRet;
}
void DoPaint(CDCHandle dc){
CRect rect;
GetClientRect(rect);
// 背景を描画
dc.FillSolidRect(rect, RGB(255, 255, 255));
// 画像を描画
if(!m_bitmap.IsNull()){
// メモリデバイスコンテキストを作成し、ビットマップを選択
CDC dcMem;
dcMem.CreateCompatibleDC(dc);
HBITMAP hOldBitmap = dcMem.SelectBitmap(m_bitmap);
// クライアント矩形と画像矩形を取得し、中央に画像を表示
CSize size;
m_bitmap.GetSize(size);
int nX = rect.Width() / 2 - size.cx / 2;
int nY = rect.Height() / 2 - size.cy / 2;
dc.BitBlt(nX, nY, size.cx, size.cy, dcMem, 0, 0, SRCCOPY);
// 元のビットマップを選択
dcMem.SelectBitmap(hOldBitmap);
}
// サイズ12ポイントの"Tahoma"フォントを作成し、選択
CFont font;
font.CreatePointFont(120, _T("Tahoma"));
HFONT hOldFont = dc.SelectFont(font);
// テキスト色を白に設定
dc.SetBkMode(TRANSPARENT);
dc.SetTextColor(RGB(255, 255, 255));
// 文字列を描画
dc.DrawText(_T("Double Buffer"), -1,
rect, DT_SINGLELINE | DT_CENTER | DT_VCENTER);
// 元のフォントを選択
dc.SelectFont(hOldFont);
}
};
|
CImageViewクラスはCDoubleBufferWindowImplから派生し、
メッセージマップにはWM_CREATEメッセージエントリとCDoubleBufferWindowImplへのチェインを追加します。
WM_CREATEメッセージハンドラではSHLoadImageFile()を呼び出してプログラムと同じフォルダにあるビットマップファイルを読み込みます。
次に、基底クラスのメンバ関数であるDoPaint()をオーバーライドします。
この関数は基底クラスで用意されているWM_PAINTメッセージハンドラから呼び出されます。
DoPaint()の引数dcはメモリデバイスコンテキストで、
dcに対して描画した画像や文字列はDoPaint()を抜けた後にまとめてクライアント領域に描画されます。
今回の例ではクライアント領域全体を白で塗りつぶし、
中央にビットマップ画像と「Double Buffer」という文字列を描画します。
次に示すのは、CImageViewクラスを使用する例です。
// stdafx.h
#pragma once
#define WINVER 0x0420
#include <atlbase.h>
#if _ATL_VER == 0x900
#define _SECURE_ATL 1
#endif
#define _WTL_USE_CSTRING
#include <atlapp.h>
extern CAppModule _Module;
#include <atlwin.h>
#include <tpcshell.h>
#include <aygshell.h>
#pragma comment(lib, "aygshell.lib")
#include <atlcrack.h>
#include <atlmisc.h>
#include <atlframe.h>
#include <atlctrls.h>
#define _WTL_CE_NO_ZOOMSCROLL
#define _WTL_CE_NO_FULLSCREEN
#include <atlwince.h>
|
// SampleProjectFrame.h
#pragma once
class CSampleProjectFrame :
public CFrameWindowImpl<CSampleProjectFrame>,
public CUpdateUI<CSampleProjectFrame>,
public CAppWindow<CSampleProjectFrame>,
public CMessageFilter, public CIdleHandler
{
public:
DECLARE_APP_FRAME_CLASS(NULL, IDR_MAINFRAME, L"Software\\WTL")
CImageView m_view;
virtual BOOL PreTranslateMessage(MSG* pMsg){
if(CFrameWindowImpl<CSampleProjectFrame>::PreTranslateMessage(pMsg))
return TRUE;
return m_view.IsWindow() ? m_view.PreTranslateMessage(pMsg) : FALSE;
}
virtual BOOL OnIdle(){
return FALSE;
}
BEGIN_UPDATE_UI_MAP(CSampleProjectFrame)
END_UPDATE_UI_MAP()
BEGIN_MSG_MAP(CSampleProjectFrame)
MSG_WM_CREATE(OnCreate)
MSG_WM_DESTROY(OnDestroy)
CHAIN_MSG_MAP(CAppWindow<CSampleProjectFrame>)
CHAIN_MSG_MAP(CUpdateUI<CSampleProjectFrame>)
CHAIN_MSG_MAP(CFrameWindowImpl<CSampleProjectFrame>)
END_MSG_MAP()
int OnCreate(LPCREATESTRUCT lpCreateStruct){
// メニューバー作成
CreateSimpleCEMenuBar(0, SHCMBF_EMPTYBAR);
// ビューを作成
m_hWndClient = m_view.Create(m_hWnd, rcDefault, NULL,
WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN);
// メッセージループにメッセージフィルタとアイドルハンドラを追加
CMessageLoop* pLoop = _Module.GetMessageLoop();
pLoop->AddMessageFilter(this);
pLoop->AddIdleHandler(this);
return 0;
}
void OnDestroy(){
// メッセージループからメッセージフィルタとアイドルハンドラを削除
CMessageLoop* pLoop = _Module.GetMessageLoop();
pLoop->RemoveMessageFilter(this);
pLoop->RemoveIdleHandler(this);
SetMsgHandled(false);
}
};
|
// SampleProject.cpp
#include "stdafx.h"
#include "resourceppc.h"
#include "View.h"
#include "SampleProjectFrame.h"
CAppModule _Module;
int WINAPI _tWinMain(HINSTANCE hInstance,
HINSTANCE /*hPrevInstance*/, LPTSTR lpstrCmdLine, int nCmdShow)
{
HRESULT hRes =
CSampleProjectFrame::ActivatePreviousInstance(hInstance, lpstrCmdLine);
if(FAILED(hRes) || S_FALSE == hRes){
return hRes;
}
hRes = ::CoInitializeEx(NULL, COINIT_MULTITHREADED);
ATLASSERT(SUCCEEDED(hRes));
AtlInitCommonControls(ICC_DATE_CLASSES);
SHInitExtraControls();
hRes = _Module.Init(NULL, hInstance);
ATLASSERT(SUCCEEDED(hRes));
int nRet = CSampleProjectFrame::AppRun(lpstrCmdLine, nCmdShow);
_Module.Term();
::CoUninitialize();
return nRet;
}
|
まず、ビューウィンドウに描画するための次のようなビットマップファイルを用意します。

次に、CImageViewクラスのインスタンスを
CSampleProjectFrameクラスのメンバ変数として宣言し、
WM_CREATEメッセージハンドラでCreate()を呼び出してビューウィンドウを作成します。
最後に、SampleProject.cppファイルでSampleProjectFrame.hヘッダの前にView.hヘッダをインクルードします。
|