ホーム ATL/WTL
ビューウィンドウ
ドキュメント種別 ATL/WTL に関する文書
最終更新日 2004/10/02
PR
 ビューウィンドウとは、フレームウィンドウのクライアント領域を覆うようにして表示される子ウィンドウのことです。 ここでは、これまで作成してきたフレームウィンドウにビューウィンドウを追加し、 そのビューウィンドウに「Hello, ATL/WTL」という文字列を描画します。


// 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)
    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);
        }
    }
};
			

// 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_HELLO, OnMenuHello)
        COMMAND_ID_HANDLER_EX(ID_MENUITEM_EXIT, OnMenuExit)
        CHAIN_MSG_MAP(CUpdateUI<CMyWindow>)         // CUpdateUIクラスへチェーン
        CHAIN_MSG_MAP(CFrameWindowImpl<CMyWindow>)  // CFrameWindowImplクラスへチェーン
    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 OnMenuHello(UINT uNotifyCode, int nID, HWND hWndCtl){
        MessageBox(_T("Hello, ATL/WTL"));
    }

    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;
}
			

 まず、ビューウィンドウのためのクラスを定義します。 上の例ではプロジェクトにView.hというヘッダファイルを追加し、 そこにCViewというクラスを定義しています。 CViewクラスはCWindowImplクラスから派生する通常のウィンドウです。 CViewクラス内では、独自のメッセージフィルタを用意し、 メッセージマップでWM_PAINTメッセージとWM_CONTEXTMENUメッセージをマッピングしています。 これら二つのメッセージは、これまでフレームウィンドウ側で処理していましたが、 ビューウィンドウによってフレームウィンドウのクライアント領域が隠れてしまうため、 ビューウィンドウ側で処理します。 よって、フレームウィンドウであるCMyWindowクラスからは、 これら二つのメッセージマップエントリ及びメッセージハンドラを削除しています。

なお、WM_CONTEXTMENUメッセージハンドラで呼び出している TrackPopupMenu()の第4引数は、親ウインドウのハンドルに変更します。

 CMyWindowクラス内では、まず、フレームウィンドウクラス名登録マクロをDECLARE_FRAME_WND_CLASS_EXから DECLARE_FRAME_WND_CLASSに変更しています。 これは、ビューウィンドウを使用することによって、 フレームウィンドウのクライアント領域に直接「Hello, ATL/WTL」という文字列を描画しなくなるため、 CS_HREDRAW | CS_VREDRAW というスタイルを指定する必要なくなるからです。

 次に、ビューウィンドウであるCViewクラスのインスタンスを、 CMyWindowクラスのメンバ変数として宣言します。 CMyWindow::OnCreate()ではこのビューウィンドウを作成し、 CFrameWindowImplクラスの基底クラスであるCFrameWindowImplBase クラスのm_hWndClientというHWND型のメンバ変数に代入します。

CFrameWindowImplクラスにはWM_SIZEメッセージハンドラが用意されています。 このため、メインウィンドウのサイズを変更したときのWM_SIZEメッセージが チェーン先のCFrameWindowImplクラスに送られると、 CFrameWindowImplクラスのWM_SIZEメッセージハンドラが呼び出されます。 このハンドラではUpdateLayout()という関数を呼び出しており、 この関数は各バーやm_hWndClientで識別されるビューウィンドウを自動的にフレームウィンドウのサイズに合わせます。

また、CMyWindowクラスのメッセージフィルタでは、 CViewクラスの独自メッセージフィルタ関数を呼び出しています。 これにより、ビューウィンドウにもメッセージフィルタリングの機会を与えています。

 hello.cppファイル内では、MainWindow.hヘッダをインクルードする前にView.hヘッダをインクルードします。