WTLはスクロールウィンドウを作成するためにCScrollWindowImplというクラステンプレートを用意しています。
これはATLのCWindowImplクラステンプレートのように基底クラスとしてのみ使用し、
テンプレート引数の種類もCWindowImplと同じです。
以下に示すのは、CScrollWindowImplをベースとしたビューウィンドウを作成する例です。
プロジェクトにView.hというヘッダファイルを追加し、そこにCScrollViewというクラスを定義します。
メニューには、ウィンドウを上下左右にスクロールするためのメニューアイテムを追加します。
// View.h
#pragma once
class CScrollView : public CScrollWindowImpl<CScrollView>
{
public:
BOOL PreTranslateMessage(MSG* pMsg){
return FALSE;
}
BEGIN_MSG_MAP(CScrollView)
MSG_WM_CREATE(OnCreate)
CHAIN_MSG_MAP(CScrollWindowImpl<CScrollView>)
CHAIN_MSG_MAP_ALT(CScrollWindowImpl<CScrollView>, 1)
END_MSG_MAP()
int OnCreate(LPCREATESTRUCT lpCreateStruct){
LRESULT lRet = DefWindowProc();
SetScrollSize(640, 480);
SetScrollLine(10, 10);
SetScrollPage(100, 100);
SetMsgHandled(false);
return lRet;
}
void DoPaint(CDCHandle dc){
// スクロールウィンドウの中央に文字列を描画
CSize size;
GetScrollSize(size);
CRect rect(0, 0, size.cx, size.cy);
dc.DrawText(_T("Hello, ATL/WTL"),
-1, rect, DT_SINGLELINE | DT_CENTER | DT_VCENTER);
}
};
|
CScrollViewクラスはCScrollWindowImplから派生し、
メッセージマップにはWM_CREATEメッセージエントリと基底クラスへのチェインを追加します。
WM_CREATEメッセージハンドラではスクロールウィンドウのサイズやスクロールバーの移動量を設定し、
SetMsgHandled(false)を呼び出します。
これは基底クラスでもWM_CREATEメッセージハンドラが用意されているためです。
次に、基底クラスであるCScrollWindowImplのメンバ関数DoPaint()をオーバーライドします。
この関数はCScrollWindowImplで用意されているWM_PAINTメッセージハンドラから呼び出され、
ここにビューへの描画コードを記述することで、スクロールバーに合わせて描画内容も自動的にスクロールされるようになります。
今回の例ではスクロールウィンドウの中央に文字列を描画します。
CScrollWindowImplは代替メッセージマップも用意しています。
これは、ビューを上下左右にスクロールするためのWM_COMMANDメッセージをマッピングしています。
WTLはスクロールウィンドウを操作するためにデフォルトで次のようなIDを用意しています。
| ID |
操作 |
| ID_SCROLL_UP |
上にスクロール |
| ID_SCROLL_DOWN |
下にスクロール |
| ID_SCROLL_PAGE_UP |
前のページ |
| ID_SCROLL_PAGE_DOWN |
次のページ |
| ID_SCROLL_TOP |
最上部 |
| ID_SCROLL_BOTTOM |
最下部 |
| ID_SCROLL_LEFT |
左にスクロール |
| ID_SCROLL_RIGHT |
右にスクロール |
| ID_SCROLL_PAGE_LEFT |
左のページ |
| ID_SCROLL_PAGE_RIGHT |
右のページ |
| ID_SCROLL_ALL_LEFT |
左の端 |
| ID_SCROLL_ALL_RIGHT |
右の端 |
このように、CScrollWindowImplにはメッセージマップが用意されており、
WM_CREATEメッセージやWM_PAINTメッセージ、その他
WM_VSCROLL、WM_HSCROLL、WM_MOUSEWHEEL、
WM_SIZE、WM_SETTINGCHANGE、WM_PRINTCLIENTメッセージをマッピングしているため、
少ない作業で基本的な機能を持ったスクロールウィンドウを作成することができます。
次に示すのは、CScrollViewクラスを使用する例です。
// stdafx.h
#pragma once
#include <atlbase.h>
#include <atlapp.h>
extern CAppModule _Module;
#include <atlwin.h>
#include <atlcrack.h>
#include <atlmisc.h>
#include <atlframe.h>
#include <atlscrl.h> // CScrollWindowImplを使用するため
|
// MainFrm.h
#pragma once
class CMainFrame : public CFrameWindowImpl<CMainFrame>,
public CUpdateUI<CMainFrame>, public CMessageFilter, public CIdleHandler
{
public:
DECLARE_FRAME_WND_CLASS(NULL, IDR_MAINFRAME)
CScrollView m_view;
virtual BOOL PreTranslateMessage(MSG* pMsg){
if(CFrameWindowImpl<CMainFrame>::PreTranslateMessage(pMsg))
return TRUE;
return m_view.PreTranslateMessage(pMsg);
}
virtual BOOL OnIdle(){
return FALSE;
}
BEGIN_UPDATE_UI_MAP(CMainFrame)
// エントリなし
END_UPDATE_UI_MAP()
BEGIN_MSG_MAP(CMainFrame)
MSG_WM_CREATE(OnCreate)
MSG_WM_DESTROY(OnDestroy)
COMMAND_ID_HANDLER_EX(ID_APP_EXIT, OnFileExit)
CHAIN_MSG_MAP(CUpdateUI<CMainFrame>)
CHAIN_MSG_MAP(CFrameWindowImpl<CMainFrame>)
CHAIN_CLIENT_COMMANDS() // ビュークラスへコマンドチェイン
END_MSG_MAP()
int OnCreate(LPCREATESTRUCT lpCreateStruct){
// ビューを作成
m_hWndClient = m_view.Create(m_hWnd, rcDefault, NULL,
WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
WS_EX_CLIENTEDGE);
// メッセージループにメッセージフィルタとアイドルハンドラを追加
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);
}
void OnFileExit(UINT uNotifyCode, int nID, CWindow wndCtl){
PostMessage(WM_CLOSE);
}
};
|
// SampleProject.cpp
#include "stdafx.h"
#include "resource.h"
#include "View.h"
#include "MainFrm.h"
CAppModule _Module;
int WINAPI _tWinMain(HINSTANCE hInstance,
HINSTANCE /*hPrevInstance*/, LPTSTR lpstrCmdLine, int nCmdShow)
{
HRESULT hRes = ::CoInitialize(NULL);
ATLASSERT(SUCCEEDED(hRes));
::DefWindowProc(NULL, 0, 0, 0L);
AtlInitCommonControls(ICC_BAR_CLASSES);
hRes = _Module.Init(NULL, hInstance);
ATLASSERT(SUCCEEDED(hRes));
CMessageLoop theLoop;
_Module.AddMessageLoop(&theLoop);
CMainFrame wndMain;
wndMain.CreateEx();
wndMain.ShowWindow(nCmdShow);
int nRet = theLoop.Run();
_Module.RemoveMessageLoop();
_Module.Term();
::CoUninitialize();
return nRet;
}
|
まず、メニューバーに次のようなメニューアイテムを追加します。
| リソース名 |
ID |
Caption |
Prompt |
| メニューアイテム(トップレベル) |
- |
表示(&V) |
- |
| メニューアイテム |
ID_SCROLL_UP |
上にスクロール(&U) |
ウィンドウを上にスクロールします。\n上にスクロール |
| メニューアイテム |
ID_SCROLL_DOWN |
下にスクロール(&D) |
ウィンドウを下にスクロールします。\n下にスクロール |
| メニューアイテム |
ID_SCROLL_LEFT |
左にスクロール(&L) |
ウィンドウを左にスクロールします。\n左にスクロール |
| メニューアイテム |
ID_SCROLL_RIGHT |
右にスクロール(&R) |
ウィンドウを右にスクロールします。\n右にスクロール |

次に、stdafx.hヘッダでは、CScrollWindowImplを使用するためにatlscrl.hヘッダをインクルードします。
次に、CScrollViewクラスのインスタンスを
CMainFrameクラスのメンバ変数として宣言し、
WM_CREATEメッセージハンドラでCreate()を呼び出してビューウィンドウを作成します。
CMainFrameクラスのメッセージマップにはコマンドチェインを追加します。
これにより、メニューアイテムのWM_COMMANDメッセージがフレームウィンドウからビューウィンドウへ転送され、
さらにCScrollViewクラスの代替メッセージマップへのチェインによってCScrollWindowImplの代替メッセージマップに転送されます。
CScrollWindowImplでは間接的にDoScroll()というメンバ関数が呼び出され、
そこでメニューアイテムのID別にスクロール処理が行われます。
最後に、SampleProject.cppファイルでMainFrm.hヘッダの前にView.hヘッダをインクルードします。
|