ホーム ATL/WTL
コマンドチェーン
ドキュメント種別 ATL/WTL に関する文書
最終更新日 2004/10/02
PR
 通常、メニューバーやツールバーからの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()