通常、メニューバーやツールバーからの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で識別されるビューウィンドウのメッセージマップにチェーンします。
次に示すのは、前回ビューウィンドウをを追加した「Hello, ATL/WTL」プログラムにコマンドチェーンを追加して、
ビューウィンドウ側でIDがID_MENUITEM_HELLOのコマンドメッセージを処理する例です。
// stdafx.h内
#include <atlbase.h>
#include <atlapp.h>
extern CAppModule _Module;
#include <atlwin.h>
#include <atlcrack.h>
#include <atlmisc.h>
#include <atlframe.h> // CFrameWindowImplクラスを使用するため
|
// View.h内
class CView : public CWindowImpl<CView>
{
public:
// メッセージフィルタ処理
BOOL PreTranslateMessage(MSG* pMsg){
return FALSE;
}
private:
// メッセージマップ
BEGIN_MSG_MAP_EX(CView)
MSG_WM_PAINT(OnPaint)
MSG_WM_CONTEXTMENU(OnContextMenu)
COMMAND_ID_HANDLER_EX(ID_MENUITEM_HELLO, OnMenuHello)
END_MSG_MAP()
void OnPaint(HDC /*hDC*/){
CPaintDC dc(m_hWnd);
CRect rect;
GetClientRect(rect);
dc.DrawText(_T("Hello, ATL/WTL"), -1,
rect, DT_SINGLELINE | DT_CENTER | DT_VCENTER);
}
void OnContextMenu(HWND hWnd, CPoint pt){
// [Shift]+[F10]キーが押された場合は座標をクライアント領域の左上に設定
if(pt.x == -1 && pt.y == -1){
pt.SetPoint(0, 0);
ClientToScreen(&pt);
}
// 座標がクライアント領域内の場合のみポップアップメニューを表示
CRect rc;
GetClientRect(&rc);
ClientToScreen(&rc);
if(rc.PtInRect(pt)){
CMenu menuPopup;
menuPopup.LoadMenu(IDR_MENU_POPUP);
menuPopup.GetSubMenu(0).TrackPopupMenu(
TPM_LEFTALIGN | TPM_TOPALIGN | TPM_LEFTBUTTON,
pt.x, pt.y,
GetParent() // 親ウィンドウのハンドル
);
}else{
SetMsgHandled(false);
}
}
void OnMenuHello(UINT uNotifyCode, int nID, HWND hWndCtl){
MessageBox(_T("Hello, ATL/WTL"));
}
};
|
// MainWindow.h内
// メインウィンドウの基底クラスをCFrameWindowImplに変更
class CMyWindow : public CFrameWindowImpl<CMyWindow>,
public CMessageFilter, public CIdleHandler, public CUpdateUI<CMyWindow>
{
public:
// ウィンドウクラス名、共通リソースIDを登録
DECLARE_FRAME_WND_CLASS(_T("Hello"), IDR_MAINFRAME)
// UI更新ハンドラマップ
BEGIN_UPDATE_UI_MAP(CMyWindow)
UPDATE_ELEMENT(ID_MENUITEM_TOPMOST, UPDUI_MENUPOPUP | UPDUI_TOOLBAR)
END_UPDATE_UI_MAP()
private:
CView m_view;
// メッセージフィルタ処理
virtual BOOL PreTranslateMessage(MSG* pMsg){
// 基底クラスのPreTranslateMessageを呼び出す
if(CFrameWindowImpl<CMyWindow>::PreTranslateMessage(pMsg))
return TRUE;
// ビューウィンドウクラスのPreTranslateMessageを呼び出す
return m_view.PreTranslateMessage(pMsg);
}
// アイドル処理
virtual BOOL OnIdle(){
UIUpdateToolBar();
return FALSE;
}
// メッセージマップ
BEGIN_MSG_MAP_EX(CMyWindow)
MSG_WM_CREATE(OnCreate)
COMMAND_ID_HANDLER_EX(ID_MENUITEM_TOPMOST, OnMenuTopmost)
COMMAND_ID_HANDLER_EX(ID_MENUITEM_EXIT, OnMenuExit)
CHAIN_MSG_MAP(CUpdateUI<CMyWindow>) // CUpdateUIクラスへチェーン
CHAIN_MSG_MAP(CFrameWindowImpl<CMyWindow>) // CFrameWindowImplクラスへチェーン
CHAIN_CLIENT_COMMANDS() // ビューウィンドウへチェーン
END_MSG_MAP()
LRESULT OnCreate(LPCREATESTRUCT lpcs){
// リバーを作成
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_MENUITEM_TOPMOST, false);
// メッセージループにメッセージフィルタとアイドルハンドラを追加
CMessageLoop* pLoop = _Module.GetMessageLoop();
pLoop->AddMessageFilter(this);
pLoop->AddIdleHandler(this);
return 0;
}
void OnMenuTopmost(UINT uNotifyCode, int nID, HWND hWndCtl){
bool bTopmost = !(UIGetState(nID) & UPDUI_CHECKED);
SetWindowPos(bTopmost ? HWND_TOPMOST : HWND_NOTOPMOST,
0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_SHOWWINDOW);
UISetCheck(nID, bTopmost);
}
void OnMenuExit(UINT uNotifyCode, int nID, HWND hWndCtl){
PostMessage(WM_CLOSE);
}
};
|
// hello.cpp内
#include "stdafx.h"
#include "resource.h"
#include "View.h"
#include "MainWindow.h"
CAppModule _Module;
int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE, LPTSTR lpCmdLine, int nCmdShow)
{
// コモンコントロール及びリバー初期化
AtlInitCommonControls(ICC_WIN95_CLASSES | ICC_COOL_CLASSES);
_Module.Init(NULL, hInstance);
CMessageLoop theLoop;
_Module.AddMessageLoop(&theLoop);
// 独自ウィンドウを作成
CMyWindow wnd;
wnd.CreateEx();
wnd.ShowWindow(nCmdShow);
wnd.UpdateWindow();
int nRet = theLoop.Run();
_Module.RemoveMessageLoop();
_Module.Term();
return nRet;
}
|
まず、IDがID_MENUITEM_HELLOのコマンドメッセージ用メッセージマップエントリと
メッセージハンドラを、CMyWindowクラスからCViewクラスへ移動します。
そして、CMyWindowクラスのメッセージマップにCHAIN_CLIENT_COMMANDS()
コマンドチェーンマクロを追加します。
これにより、メインウィンドウであるCMyWindowクラスで処理されない
ID_MENUITEM_HELLOコマンドメッセージは、
ビューウィンドウであるCViewクラスに送られ、
CViewクラスのOnMenuHello()が呼び出されるようになります。
なお、上の例ではCHAIN_CLIENT_COMMANDS()マクロを使用しましたが、
CHAIN_COMMANDS_MEMBER()マクロでも同じことができます。
その場合は、ビューウィンドウクラスのインスタンスを指定します。
CView m_view; // ビューウィンドウのインスタンス
...
...
// CMyWindowクラスのメッセージマップ
BEGIN_MSG_MAP_EX(CMyWindow)
MSG_WM_CREATE(OnCreate)
COMMAND_ID_HANDLER_EX(ID_MENUITEM_TOPMOST, OnMenuTopmost)
COMMAND_ID_HANDLER_EX(ID_MENUITEM_EXIT, OnMenuExit)
CHAIN_MSG_MAP(CUpdateUI<CMyWindow>) // CUpdateUIクラスへチェーン
CHAIN_MSG_MAP(CFrameWindowImpl<CMyWindow>) // CFrameWindowImplクラスへチェーン
CHAIN_COMMANDS_MEMBER(m_view) // ビューウィンドウへチェーン
END_MSG_MAP()
|
|