ホーム ATL/WTL
スクロールコンテナ
ドキュメント種別 ATL/WTL に関する文書
最終更新日 2005/08/27
PR
 スクロールコンテナとは、スクロール機能付きのコンテナウィンドウのことです。 スクロールコンテナを使用すると、スクロール機能を持たないウィンドウに簡単にスクロール機能を追加できます。 WTLはスクロールコンテナを作成するためにCScrollContainerクラスを用意しています。

 以下に示すのは、CScrollContainerクラスを使用してスクロールコンテナを作成する例です。 前回作成したスプリッタウィンドウの右ペインを、スクロールコンテナにします。


// stdafx.h内
#include <atlbase.h>
#include <atlapp.h>
extern CAppModule _Module;
#include <atlwin.h>

#include <atlcrack.h>
#include <atlmisc.h>
#include <atlctrls.h>
#include <atlctrlx.h>
#include <atlscrl.h>    // スクロールコンテナを使用するため
#include <atlframe.h>
#include <atlsplit.h>
			

// LeftView.h内
class CLeftView : public CWindowImpl<CLeftView, CListBox>
{
public:
    DECLARE_WND_SUPERCLASS(NULL, CListBox::GetWndClassName())

    BOOL PreTranslateMessage(MSG* pMsg){
        return FALSE;
    }

    // メッセージマップ
    BEGIN_MSG_MAP_EX(CLeftView)
        MSG_WM_CREATE(OnCreate)
    END_MSG_MAP()

    LRESULT OnCreate(LPCREATESTRUCT lpcs){
        LRESULT lRet = DefWindowProc();

        SetFont(AtlGetDefaultGuiFont());

        CClientDC dc(m_hWnd);
        EnumFontFamilies(dc, NULL,
            (FONTENUMPROC)FontProc, (LPARAM)this);

        return lRet;
    }

    static int CALLBACK FontProc(ENUMLOGFONT *lpelf,
        NEWTEXTMETRIC *lpntm, int nFontType, LPARAM lParam)
    {
        CLeftView* pList = (CLeftView*)lParam;

        // TrueTypeフォント名のみリストに追加
        if(nFontType & TRUETYPE_FONTTYPE)
            pList->AddString(lpelf->elfLogFont.lfFaceName);

        return 1;
    }
};
			

// RightView.h内
class CRightView : public CWindowImpl<CRightView, CStatic>
{
    CFont m_font;

public:
    DECLARE_WND_SUPERCLASS(NULL, CStatic::GetWndClassName())

    BOOL PreTranslateMessage(MSG* pMsg){
        return FALSE;
    }

    // メッセージマップ
    BEGIN_MSG_MAP_EX(CRightView)
        MSG_WM_SIZE(OnSize)
    END_MSG_MAP()

    void OnSize(UINT nType, CSize size){
        Invalidate();
    }

    void SetFontName(LPCTSTR lpszFontName){
        if(!m_font.IsNull())
            m_font.DeleteObject();
        m_font.CreatePointFont(200, lpszFontName);
        SetFont(m_font);

        SetWindowText(lpszFontName);
    }
};
			

// mainfrm.h内
class CMainFrame : public CFrameWindowImpl<CMainFrame>,
    public CUpdateUI<CMainFrame>, public CMessageFilter, public CIdleHandler
{
public:
    DECLARE_FRAME_WND_CLASS(NULL, IDR_MAINFRAME)

    CSplitterWindow  m_wndSplitter;      // スプリッタウィンドウ
    CPaneContainer m_painContainer;      // ペインコンテナ
    CScrollContainer m_scrollContainer;  // スクロールコンテナ
    CLeftView m_viewLeft;                // 左ペイン
    CRightView m_viewRight;              // 右ペイン

    virtual BOOL PreTranslateMessage(MSG* pMsg){
        if(CFrameWindowImpl<CMainFrame>::PreTranslateMessage(pMsg))
            return TRUE;

        // 左ペインのPreTranslateMessageを呼び出す
        if(m_viewLeft.PreTranslateMessage(pMsg))
            return TRUE;

        // 右ペインのPreTranslateMessageを呼び出す
        return m_viewRight.PreTranslateMessage(pMsg);
    }

    virtual BOOL OnIdle(){
        UIUpdateStatusBar();
        return FALSE;
    }

    BEGIN_UPDATE_UI_MAP(CMainFrame)
        // エントリなし
    END_UPDATE_UI_MAP()

    BEGIN_MSG_MAP_EX(CMainFrame)
        MSG_WM_CREATE(OnCreate)
        COMMAND_CODE_HANDLER_EX(LBN_SELCHANGE, OnListSelChange)
        COMMAND_ID_HANDLER_EX(ID_PANE_CLOSE, OnMenuChangeView)
        COMMAND_ID_HANDLER_EX(ID_MENUITEM_CHANGEVIEW, OnMenuChangeView)
        COMMAND_ID_HANDLER_EX(ID_APP_EXIT, OnFileExit)
        CHAIN_MSG_MAP(CUpdateUI<CMainFrame>)
        CHAIN_MSG_MAP(CFrameWindowImpl<CMainFrame>)
    END_MSG_MAP()

    LRESULT OnCreate(LPCREATESTRUCT lpcs){
        // ステータスバーを作成
        CreateSimpleStatusBar();
        UIAddStatusBar(m_hWndStatusBar);

        // スプリッタウィンドウを作成
        m_wndSplitter.Create (m_hWnd, rcDefault, NULL,
            WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS);

        // スプリッタウィンドウ拡張スタイルを設定
        m_wndSplitter.SetSplitterExtendedStyle(0);

        // ペインコンテナを作成
        m_painContainer.Create(m_wndSplitter, _T("フォント名"));

        // 左ペインのビューウィンドウを作成
        m_viewLeft.Create(m_painContainer, rcDefault, NULL,
            WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_VSCROLL | 
            LBS_NOINTEGRALHEIGHT | LBS_NOTIFY | LBS_WANTKEYBOARDINPUT | LBS_SORT,
            WS_EX_STATICEDGE);

        m_painContainer.SetClient(m_viewLeft);
        m_wndSplitter.SetSplitterPane(SPLIT_PANE_LEFT, m_painContainer);

        // スクロールコンテナを作成
        m_scrollContainer.Create(m_wndSplitter, rcDefault, NULL,
            WS_CHILD | WS_VISIBLE , WS_EX_CLIENTEDGE);

        // 右ペインのビューウィンドウを作成
        m_viewRight.Create(m_scrollContainer, CRect(0, 0, 320, 240), NULL,
            WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | 
            SS_CENTER | SS_CENTERIMAGE, 0);

        m_scrollContainer.SetClient(m_viewRight);
        m_wndSplitter.SetSplitterPane(SPLIT_PANE_RIGHT, m_scrollContainer);

        m_hWndClient = m_wndSplitter;
        UpdateLayout();
 
        // 分割バーの位置を設定
        m_wndSplitter.SetSplitterPos(120);

        // メッセージループにメッセージフィルタとアイドルハンドラを追加
        CMessageLoop* pLoop = _Module.GetMessageLoop();
        pLoop->AddMessageFilter(this);
        pLoop->AddIdleHandler(this);

        return 0;
    }

    void OnListSelChange(UINT uNotifyCode, int nID, HWND hWndCtl){
        // 現在選択されているアイテムを取得
        int nIndex = m_viewLeft.GetCurSel();
        if(nIndex != LB_ERR){
            CString strText;
            m_viewLeft.GetText(nIndex, strText);
            
            // 取得した文字列を右ペインに設定
            m_viewRight.SetFontName(strText);
        }
    }

    void OnMenuChangeView(UINT uNotifyCode, int nID, HWND hWndCtl){
        if(m_wndSplitter.GetSinglePaneMode() == SPLIT_PANE_RIGHT)
            m_wndSplitter.SetSinglePaneMode(SPLIT_PANE_NONE);    // 両ペイン表示
        else
            m_wndSplitter.SetSinglePaneMode(SPLIT_PANE_RIGHT);   // 右ペインのみ表示
    }

    void OnFileExit(UINT uNotifyCode, int nID, HWND hWndCtl){
        PostMessage(WM_CLOSE);
    }
};
			

// app.cpp内
#include "stdafx.h"

#include "resource.h"

#include "LeftView.h"
#include "RightView.h"
#include "MainFrm.h"

CAppModule _Module;

int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE, LPTSTR lpCmdLine, int nCmdShow)
{
    HRESULT hRes = ::CoInitialize(NULL);
    ATLASSERT(SUCCEEDED(hRes));

    ::DefWindowProc(NULL, 0, 0, 0L);

    AtlInitCommonControls(ICC_COOL_CLASSES | ICC_WIN95_CLASSES);

    hRes = _Module.Init(NULL, hInstance);
    ATLASSERT(SUCCEEDED(hRes));

    int nRet = 0;
    // BLOCK: アプリケーション実行
    {
        CMessageLoop theLoop;
        _Module.AddMessageLoop(&theLoop);

        CMainFrame wnd;
        wnd.CreateEx();
        wnd.ShowWindow(nCmdShow);
        wnd.UpdateWindow();

        nRet = theLoop.Run();

        _Module.RemoveMessageLoop();
    }

    _Module.Term();
    ::CoUninitialize();

    return nRet;
}
			

 まず、stdafx.h内では、CScrollContainerクラスを使用するためにatlscrl.hヘッダをインクルードします。

 次に、CMainFrameクラス内でCScrollContainer クラスのインスタンスをメンバ変数に追加します。 CMainFrameクラスのWM_CREATEメッセージハンドラでは、 まず、スプリッタウィンドウを親としてスクロールコンテナを作成します。 この時、Create()の第1引数には親ウィンドウであるスプリッタウィンドウのハンドルを指定します。

 次に、スクロールコンテナを親として右ペインのビューウィンドウを作成します。 今回の例ではCStaticクラスの派生クラスを定義し、 幅320、高さ240のサイズでウィンドウを作成しています。

 次に、ビューウィンドウのハンドルをSetClientを呼び出してスクロールコンテナにセットします。 さらにそのスクロールコンテナをSetSplitterPane()によってスプリッタウィンドウの右ペインにセットします。

 このように、ビューウィンドウの親はスクロールコンテナであり、 スクロールコンテナの親はスプリッタウィンドウであり、 スプリッタウィンドウの親はフレームウィンドウです。 次に示すのはWTLのスプリッタウィンドウとスクロールコンテナを使ったアプリケーションのイメージ図です。


 ところで、スクロールコンテナのメッセージマップにも、 スプリッタウィンドウの場合と同様にFORWARD_NOTIFICATIONS()がエントリされています。 このため、ビューウィンドウで発生したコマンドメッセージや通知メッセージは、 その親であるスクロールコンテナへ送られ、さらにスプリッタウィンドウへ転送され、 最後にフレームウィンドウへ届きます。