通常、メニューバーやツールバーからのWM_COMMANDメッセージは、
メインウィンドウのメッセージマップが処理します。
しかし、特定のクラスやビューウィンドウのメッセージマップで処理したい場合もあります。
そのような時は、WTLのコマンドチェインマクロを使用します。
ATLもいくつかのチェインマクロを用意していますが、
コマンドチェインマクロはWM_COMMANDメッセージ専用のチェインマクロです。
WTLは次のようなコマンドチェインマクロを用意しています。
CHAIN_COMMANDS(基底クラス名)
WM_COMMANDメッセージを、指定された基底クラスのメッセージマップにチェインします。
CHAIN_COMMANDS_MEMBER(メンバ変数名)
WM_COMMANDメッセージを、指定されたメンバ変数のメッセージマップにチェインします。
CHAIN_COMMANDS_ALT(基底クラス名, 代替メッセージマップ番号)
WM_COMMANDメッセージを、指定された基底クラスの代替メッセージマップにチェインします。
CHAIN_COMMANDS_ALT_MEMBER(メンバ変数名, 代替メッセージマップ番号)
WM_COMMANDメッセージを、指定されたメンバ変数の代替メッセージマップにチェインします。
CHAIN_CLIENT_COMMANDS()
WM_COMMANDメッセージを、m_hWndClientで識別されるビューウィンドウのメッセージマップにチェインします。
次に示すのは、前回のビューウィンドウを追加したプログラムにコマンドチェインを追加し、
ビューウィンドウ側でリソースIDがID_APP_ABOUTのWM_COMMANDメッセージを処理する例です。
// 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>
|
// View.h
#pragma once
class CView : public CWindowImpl<CView>
{
public:
// メッセージフィルタ処理
BOOL PreTranslateMessage(MSG* pMsg){
return FALSE;
}
// メッセージマップ
BEGIN_MSG_MAP(CView)
MSG_WM_PAINT(OnPaint)
COMMAND_ID_HANDLER_EX(ID_APP_ABOUT, OnAbout)
END_MSG_MAP()
void OnPaint(CDCHandle /*dc*/){
CPaintDC dc(m_hWnd);
CRect rect;
GetClientRect(rect);
dc.DrawText(_T("Hello, ATL/WTL"), -1,
rect, DT_SINGLELINE | DT_CENTER | DT_VCENTER);
}
void OnAbout(UINT uNotifyCode, int nID, CWindow wndCtl){
MessageBox(_T("ATL/WTLプログラミング 第2版"));
}
};
|
// MainWindow.h
#pragma once
class CMainWindow : public CFrameWindowImpl<CMainWindow>,
public CMessageFilter, public CIdleHandler, public CUpdateUI<CMainWindow>
{
public:
// ウィンドウクラス名、共通リソースIDを登録
DECLARE_FRAME_WND_CLASS(_T("Hello"), IDR_MAINFRAME)
CView m_view;
virtual BOOL PreTranslateMessage(MSG* pMsg){
// 基底クラスのPreTranslateMessageを呼び出す
if(CFrameWindowImpl<CMainWindow>::PreTranslateMessage(pMsg))
return TRUE;
// ビューウィンドウクラスのPreTranslateMessageを呼び出す
return m_view.PreTranslateMessage(pMsg);
}
virtual BOOL OnIdle(){
UIUpdateToolBar();
return FALSE;
}
BEGIN_UPDATE_UI_MAP(CMainWindow)
UPDATE_ELEMENT(ID_VIEW_TOPMOST, UPDUI_MENUPOPUP | UPDUI_TOOLBAR)
END_UPDATE_UI_MAP()
BEGIN_MSG_MAP(CMainWindow)
MSG_WM_CREATE(OnCreate)
MSG_WM_DESTROY(OnDestroy)
COMMAND_ID_HANDLER_EX(ID_VIEW_TOPMOST, OnViewTopmost)
COMMAND_ID_HANDLER_EX(ID_APP_EXIT, OnFileExit)
CHAIN_MSG_MAP(CUpdateUI<CMainWindow>) // CUpdateUIへチェイン
CHAIN_MSG_MAP(CFrameWindowImpl<CMainWindow>) // CFrameWindowImplへチェイン
CHAIN_CLIENT_COMMANDS() // ビューウィンドウへチェイン
END_MSG_MAP()
int OnCreate(LPCREATESTRUCT lpCreateStruct){
// リバーを作成
CreateSimpleReBar();
// ツールバーを作成してバンドに追加
HWND hWndToolBar = CreateSimpleToolBarCtrl(m_hWnd,
IDR_MAINFRAME, FALSE, ATL_SIMPLE_TOOLBAR_PANE_STYLE);
AddSimpleReBarBand(hWndToolBar);
UIAddToolBar(hWndToolBar);
// ステータスバーを作成
CreateSimpleStatusBar();
// ビューウィンドウを作成
m_hWndClient = m_view.Create(m_hWnd, rcDefault, NULL,
WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
WS_EX_CLIENTEDGE);
// [常に手前に表示]メニューのチェックマーク設定
UISetCheck(ID_VIEW_TOPMOST, false);
// メッセージループにメッセージフィルタとアイドルハンドラを追加
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 OnViewTopmost(UINT uNotifyCode, int nID, CWindow wndCtl){
bool bTopmost = !(UIGetState(ID_VIEW_TOPMOST) & UPDUI_CHECKED);
SetWindowPos(bTopmost ? HWND_TOPMOST : HWND_NOTOPMOST,
0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_SHOWWINDOW);
UISetCheck(ID_VIEW_TOPMOST, bTopmost);
}
void OnFileExit(UINT uNotifyCode, int nID, CWindow wndCtl){
PostMessage(WM_CLOSE);
}
};
|
// SampleProject.cpp
#include "stdafx.h"
#include "resource.h"
#include "View.h"
#include "MainWindow.h"
CAppModule _Module;
int APIENTRY _tWinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPTSTR lpCmdLine,
int nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
AtlInitCommonControls(ICC_WIN95_CLASSES | ICC_COOL_CLASSES);
_Module.Init(NULL, hInstance);
CMessageLoop theLoop;
_Module.AddMessageLoop(&theLoop);
CMainWindow wnd;
wnd.CreateEx();
wnd.ShowWindow(nCmdShow);
wnd.UpdateWindow();
int nRet = theLoop.Run();
_Module.RemoveMessageLoop();
_Module.Term();
return nRet;
}
|
まず、リソースIDがID_APP_ABOUTのWM_COMMANDメッセージ用メッセージマップエントリおよびメッセージハンドラを、
CMainWindowクラスからCViewクラスへ移動します。
そして、CMainWindowクラスのメッセージマップにCHAIN_CLIENT_COMMANDS()
コマンドチェインマクロを追加します。
これにより、リソースIDがID_APP_ABOUTのWM_COMMANDメッセージは、
ビューウィンドウであるCViewクラスに送られ、
CViewクラスのOnAbout()が呼び出されるようになります。
なお、今回の例ではCHAIN_CLIENT_COMMANDS()マクロを使用しましたが、
CHAIN_COMMANDS_MEMBER()マクロを使用することもできます。
しかし、CHAIN_COMMANDS_MEMBER()マクロを使用すると、
チェイン先のビューウィンドウがWTLメッセージクラッカーを使用している場合にアサートが発生します。
// MainWindow.hのメッセージマップ
// このままではチェイン先のビューウィンドウでアサートが発生
BEGIN_MSG_MAP(CMainWindow)
MSG_WM_CREATE(OnCreate)
MSG_WM_DESTROY(OnDestroy)
COMMAND_ID_HANDLER_EX(ID_VIEW_TOPMOST, OnViewTopmost)
COMMAND_ID_HANDLER_EX(ID_APP_EXIT, OnFileExit)
CHAIN_MSG_MAP(CUpdateUI<CMainWindow>) // CUpdateUIへチェイン
CHAIN_MSG_MAP(CFrameWindowImpl<CMainWindow>) // CFrameWindowImplへチェイン
CHAIN_COMMANDS_MEMBER(m_view) // ビューウィンドウへチェイン
END_MSG_MAP()
|
これを解消するためには、次のようにビューウィンドウのBEGIN_MSG_MAPマクロをBEGIN_MSG_MAP_EXマクロに変更します。
// View.hのメッセージマップ
BEGIN_MSG_MAP_EX(CView)
MSG_WM_PAINT(OnPaint)
COMMAND_ID_HANDLER_EX(ID_APP_ABOUT, OnAbout)
END_MSG_MAP()
|
または、ビューウィンドウでWTLメッセージクラッカーを使用しないように変更します。
// View.hのメッセージマップ
BEGIN_MSG_MAP(CView)
MESSAGE_HANDLER(WM_PAINT, OnPaint)
COMMAND_ID_HANDLER(ID_APP_ABOUT, OnAbout)
END_MSG_MAP()
|
|