ホーム ATL/WTL
タブ
ドキュメント種別 ATL/WTL に関する文書
最終更新日 2004/07/13
PR
 以下に示すのは、タブコントロールをベースとしたビューウィンドウを作成する例です。 メニューバーの[ファイル]から[新規作成]を実行するとタブを追加し、[閉じる]を実行するとカレントタブ(選択されているタブ)を削除します。 各タブにはエディットコントロールを表示します。


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

#include <atlcrack.h>
#include <atlmisc.h>
#include <atlctrls.h>
#include <atlframe.h>
			

// view.h内
class CTabView : public CWindowImpl<CTabView, CTabCtrl>
{
public:
    DECLARE_WND_SUPERCLASS(NULL, CTabCtrl::GetWndClassName())

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

private:
    // メッセージマップ
    BEGIN_MSG_MAP_EX(CTabView)
        MSG_WM_CREATE(OnCreate)
        MSG_WM_SIZE(OnSize)
        MSG_WM_DESTROY(OnDestroy)
        COMMAND_ID_HANDLER_EX(ID_FILE_NEW, OnFileNew)
        COMMAND_ID_HANDLER_EX(ID_FILE_CLOSE, OnFileClose)
        REFLECTED_NOTIFY_CODE_HANDLER_EX(TCN_SELCHANGING, OnTabSelChanging)
        REFLECTED_NOTIFY_CODE_HANDLER_EX(TCN_SELCHANGE, OnTabSelChange)
        DEFAULT_REFLECTION_HANDLER()
    END_MSG_MAP()

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

        SetFont(AtlGetDefaultGuiFont());

        return lRet;
    }

    void OnSize(UINT uType, CSize size){
        // カレントタブ内のエディットコントロールをリサイズ
        int nIndex = GetCurSel();
        if(nIndex != -1)
            ResizeEdit(nIndex);

        SetMsgHandled(false);
    }

    void OnDestroy(){
        // すべてのタブを削除
        int nCount = GetItemCount();
        for(int i=nCount-1; i>=0; i--)
            RemoveTab(i);

        SetMsgHandled(false);
    }

    void OnFileNew(UINT uNotifyCode, int nID, HWND hWndCtl){
        // タブを追加
        AddTab();
    }

    void OnFileClose(UINT uNotifyCode, int nID, HWND hWndCtl){
        // カレントタブを削除
        int nIndex = GetCurSel();
        if(nIndex != -1)
            RemoveTab(nIndex);
    }

    LRESULT OnTabSelChanging(LPNMHDR pnmh){
        // カレントタブ内のエディットコントロールを非表示にする
        ShowCurEdit(false);
        return 0;
    }

    LRESULT OnTabSelChange(LPNMHDR pnmh){
        // カレントタブ内のエディットコントロールを表示する
        ShowCurEdit(true);
        return 0;
    }

    void AddTab(){
        // カレントタブ内のエディットコントロールを非表示にする
        ShowCurEdit(false);

        // タブ名を作成
        static int nTitle = 1;
        CString strTitle;
        strTitle.Format(_T("タブ%d"), nTitle++);

        // タブに表示するエディットコントロールを作成
        CEdit* pEdit = new CEdit;
        pEdit->Create(m_hWnd, rcDefault, NULL,
            WS_CHILD | WS_VISIBLE | WS_HSCROLL | WS_VSCROLL | 
            ES_AUTOHSCROLL | ES_AUTOVSCROLL | ES_MULTILINE | ES_NOHIDESEL | ES_SAVESEL, 
            WS_EX_CLIENTEDGE);
        pEdit->SetFont(AtlGetDefaultGuiFont());

        // タブを挿入
        TCITEM ti;
        ti.mask = TCIF_TEXT | TCIF_PARAM;
        ti.pszText = (LPTSTR)(LPCTSTR)strTitle;
        ti.lParam = (LPARAM)pEdit;
        int nIndex = InsertItem(GetItemCount(), &ti);

        // 挿入したタブをカレントにしてエディットコントロールにフォーカスを与える
        SetCurSel(nIndex);
        pEdit->SetFocus();

        // エディットコントロールをリサイズ
        ResizeEdit(nIndex);
    }

    void RemoveTab(int nIndex){
        CEdit* pEdit = GetEditCtrl(nIndex);
        pEdit->DestroyWindow();
        delete pEdit;
        DeleteItem(nIndex);

        // カレントタブを先頭のタブに変更
        SetCurSel(0);
        ShowCurEdit(true);
    }

    CEdit* GetEditCtrl(int nIndex){
        // タブに関連付けられたエディットコントロールへのポインタを取得
        TCITEM ti;
        ti.mask = TCIF_PARAM;
        GetItem(nIndex, &ti);
        return (CEdit*)ti.lParam;
    }

    void ResizeEdit(int nIndex){
        CRect rcTabClient;
        GetClientRect(rcTabClient);
        AdjustRect(FALSE, rcTabClient);

        CEdit* pEdit = GetEditCtrl(nIndex);
        pEdit->MoveWindow(rcTabClient);
    }

    void ShowCurEdit(bool bShow){
        int nIndex = GetCurSel();
        if(nIndex != -1){
            CEdit* pEdit = GetEditCtrl(nIndex);
            if(bShow){
                ResizeEdit(nIndex);
                pEdit->ShowWindow(SW_SHOW);
                pEdit->SetFocus();
            }else{
                pEdit->ShowWindow(SW_HIDE);
            }
        }
    }
};
			

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

    CTabView m_view;

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

        return m_view.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_ID_HANDLER_EX(ID_APP_EXIT, OnMenuExit)
        CHAIN_MSG_MAP(CUpdateUI<CMainFrame>)
        CHAIN_MSG_MAP(CFrameWindowImpl<CMainFrame>)
        CHAIN_CLIENT_COMMANDS()    // ビュークラスへコマンドチェーン
        REFLECT_NOTIFICATIONS()    // メッセージリフレクション
    END_MSG_MAP()

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

        // ビューを作成
        m_hWndClient = m_view.Create(m_hWnd, rcDefault, NULL,
            WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
            WS_EX_CLIENTEDGE);

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

        return 0;
    }

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

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

#include "resource.h"

#include "view.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;
}
			

 まず、メニューバーリソースの[ファイル]以下に[新規作成]と[閉じる]メニューアイテムを追加し、 IDとしてそれぞれID_FILE_NEWID_FILE_CLOSEを設定します。

 次に、stdafx.h内では、コントロール用クラスを使用するためにatlctrls.hヘッダをインクルードします。

 次に、ビューウィンドウのためのクラスを定義します。 上の例ではプロジェクトにview.hというヘッダファイルを追加し、 そこにCWindowImplクラスから派生したCTabViewというクラスを定義しています。 CWindowImplクラスの第2引数には、ベースとなるCTabCtrlクラスを指定します。

 CTabViewクラス内では、 タブとタブ内に表示するエディットコントロールを操作するために、 独自のメンバ関数をいくつか用意します。 AddTab()はタブを追加します。 各タブにはエディットコントロールへのポインタを関連付けます。 RemoveTab()は指定したタブを削除します。 タブに関連付けられたエディットコントロールへのポインタも削除します。 GetEditCtrl()は指定したタブに関連付けられたエディットコントロールへのポインタを取得します。 ResizeEdit()は指定したエディットコントロールのサイズをタブサイズに合うように変更します。 ShowCurEdit()はカレントタブ内のエディットコントロールの表示/非表示を切り換えます。 引数にtrueを指定した場合は表示、falseを指定した場合は非表示にします。

 次に、WM_CREATEメッセージハンドラを追加し、 始めにDefWindowProc()を呼び出してから、 デフォルトのGUIフォントを設定します。

 次に、WM_SIZEメッセージハンドラを追加し、 ResizeEdit()を呼び出してウィンドウサイズが変更されるたびにタブ内のエディットコントロールのサイズを変更します。

 次に、WM_DESTROYメッセージハンドラを追加し、 すべてのタブに対してRemoveTab()を呼び出します。

 次に、IDがID_FILE_NEWのコマンドメッセージハンドラを追加し、 AddTab()を呼び出してタブを追加します。

 次に、IDがID_FILE_CLOSEのコマンドメッセージハンドラを追加し、 RemoveTab()を呼び出して現在のタブを削除します。

 次に、通知コードがTCN_SELCHANGINGのタブコントロール用通知メッセージハンドラを追加します。 このメッセージハンドラはタブが切り替わる直前に呼び出されますが、 ここではShowCurEdit()にfalseを指定して呼び出すことで、 切り替わる直前のタブのエディットコントロールを非表示にします。

 次に、通知コードがTCN_SELCHANGEのタブコントロール用通知メッセージハンドラを追加します。 このメッセージハンドラはタブが切り替わった直後に呼び出されますが、 ここではShowCurEdit()にtrueを指定して呼び出すことで、 切り替わった直後のタブのエディットコントロールを表示します。