ホーム ATL/WTL
テーマ
ドキュメント種別 ATL/WTL に関する文書
最終更新日 2007/03/18
PR
 Windows XP ではテーマを使用できます。WTLではテーマ関連Win32APIを CThemeImplクラステンプレートでカプセル化しています。

 次に示すのは、CThemeImplクラステンプレートを使用する例です。 ダイアログに配置したオーナードローボタンを、 テーマが有効な場合は Windows XP スタイルで描画し、 テーマが無効な場合はクラシックスタイルで描画します。



 まず、オーナードローボタンを定義するために、 プロジェクトにThemeButton.hというヘッダファイルを追加し、 そこにCThemeButtonというクラスを定義します。

// ThemeButton.h
#pragma once

class CThemeButton : public CWindowImpl<CThemeButton>, 
    public COwnerDraw<CThemeButton>, public CThemeImpl<CThemeButton>
{
public:
    DECLARE_WND_SUPERCLASS(_T("ThemeButton"), _T("BUTTON"))

    bool m_hover;

    BEGIN_MSG_MAP(CThemeButton)
        CHAIN_MSG_MAP(CThemeImpl<CThemeButton>)
        MSG_WM_MOUSEMOVE(OnMouseMove)
        MSG_WM_MOUSELEAVE(OnMouseLeave)
        CHAIN_MSG_MAP_ALT(COwnerDraw<CThemeButton>, 1)
        DEFAULT_REFLECTION_HANDLER()
    END_MSG_MAP()

    BOOL SubclassWindow(HWND hWnd){
        m_hover = false;
        
#if (_MSC_VER >= 1300)
        BOOL bRet = ATL::CWindowImpl<CThemeButton>::SubclassWindow(hWnd);
#else // !(_MSC_VER >= 1300)
        typedef ATL::CWindowImpl<CThemeButton>   _baseClass;
        BOOL bRet = _baseClass::SubclassWindow(hWnd);
#endif // !(_MSC_VER >= 1300)

        OpenThemeData(L"Button");

        return bRet;
    }

    void OnMouseMove(UINT nFlags, CPoint point){
        if(!m_hover){
            TRACKMOUSEEVENT tme;
            tme.cbSize = sizeof(TRACKMOUSEEVENT);
            tme.dwFlags = TME_LEAVE;
            tme.hwndTrack = m_hWnd;
            tme.dwHoverTime = HOVER_DEFAULT;
            TrackMouseEvent(&tme);

            m_hover = true;
            Invalidate();
        }
    }

    void OnMouseLeave(){
        if(m_hover){
            m_hover = false;
            Invalidate();
        }
    }

    void DrawItem(LPDRAWITEMSTRUCT lpdis){
        CDCHandle dc = lpdis->hDC;
        dc.SaveDC();

        if(IsThemeNull()){
            TCHAR szText[256];
            GetWindowText(szText, sizeof(szText)/sizeof(TCHAR));

            // キャプション表示領域を初期化
            CRect rcCaptionArea = lpdis->rcItem;
            rcCaptionArea.OffsetRect(0, -1);

            // ボタン描画
            UINT state = DFCS_BUTTONPUSH;
            if(lpdis->itemState & ODS_SELECTED){
                state |= DFCS_PUSHED;

                // キャプション矩形調整
                rcCaptionArea.OffsetRect(1, 1);
            }
            dc.DrawFrameControl(&lpdis->rcItem, DFC_BUTTON, state);

            // フォーカス矩形描画
            if(lpdis->itemState & ODS_FOCUS){
                CRect rcFocus = lpdis->rcItem;
                rcFocus.InflateRect(-4, -4);
                dc.DrawFocusRect(rcFocus);
            }

            // キャプション描画
            dc.SetTextColor(GetSysColor(COLOR_BTNTEXT));
            dc.SetBkMode(TRANSPARENT);
            dc.DrawText(szText, -1,
                rcCaptionArea, DT_SINGLELINE | DT_CENTER | DT_VCENTER);
        }else{
            WCHAR szText[256];
            GetWindowTextW(szText, sizeof(szText)/sizeof(WCHAR));

            // ボタン描画
            int state = PBS_NORMAL;
            if(lpdis->itemState & ODS_SELECTED){
                state = PBS_PRESSED;
            }else if(m_hover){
                state = PBS_HOT;
            }
            DrawThemeBackground(dc, BP_PUSHBUTTON, state, &lpdis->rcItem);

            // フォーカス矩形描画
            if(lpdis->itemState & ODS_FOCUS){
                CRect rcFocus = lpdis->rcItem;
                rcFocus.DeflateRect(4, 4);
                dc.DrawFocusRect(rcFocus);
            }

            // キャプション描画
            DrawThemeText(dc, BP_PUSHBUTTON, state, szText, -1, 
                DT_SINGLELINE | DT_CENTER | DT_VCENTER, 0, &lpdis->rcItem);
        }

        dc.RestoreDC(-1);
    }
};
			

まず、CThemeButtonクラスはテーマをサポートするため、 CThemeImplを基底クラスに追加します。

次に、CThemeButtonクラスのメンバ関数であるSubclassWindow()では、 メンバ変数に初期値を設定し、基底クラスのSubclassWindow()を呼び出してコントロールをサブクラス化してから、 CThemeImplのメンバ関数であるOpenThemeData()を呼び出します。

Windows XP スタイルのボタンは、マウスカーソルがボタン上にある場合に状態が変化します。 そのため、CThemeButtonクラスにはWM_MOUSEMOVEメッセージハンドラと WM_MOUSELEAVEメッセージハンドラを追加し、 そこでInvalidate()を呼び出してボタンの外観を更新します。

DrawItem()ではボタンを描画します。 まず、CThemeImplのメンバ関数であるIsThemeNull()を呼び出してNULLが返ってきた場合はテーマが無効であると判断し、 クラシックスタイルのボタンを描画します。 NULL以外の場合はDrawThemeBackground()DrawThemeText()を呼び出して、 Windows XP スタイルのボタンを描画します。

なお、システムのテーマが変更された場合や、 コントロールが破棄された場合はCloseThemeData()を呼び出す必要がありますが、 CThemeButtonクラスではそのようなコードはありません。 これは、CThemeImplのメッセージマップにWM_THEMECHANGEDメッセージハンドラや WM_DESTROYメッセージハンドラが用意されており、そこでCloseThemeData()が呼び出されるためです。

 次に示すのは、CThemeButtonクラスを使用する例です。

プロジェクトファイル ダウンロード
// stdafx.h
#pragma once

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

#include <atlcrack.h>
#include <atlmisc.h>
#include <atlframe.h>
#include <atltheme.h>  // CThemeImplを使用するため
			

// MainDlg.h
#pragma once

class CMainDlg : public CDialogImpl<CMainDlg>
{
public:
    enum { IDD = IDD_MAINDLG };

    CThemeButton m_button;

    BEGIN_MSG_MAP(CMainDlg)
        MSG_WM_INITDIALOG(OnInitDialog)
        COMMAND_ID_HANDLER_EX(IDOK, OnOK)
        COMMAND_ID_HANDLER_EX(IDCANCEL, OnCancel)
        REFLECT_NOTIFICATIONS()
    END_MSG_MAP()

    BOOL OnInitDialog(CWindow wndFocus, LPARAM lInitParam){
        // スクリーンの中央に配置
        CenterWindow();

        // 大きいアイコン設定
        HICON hIcon = AtlLoadIconImage(IDR_MAINFRAME, LR_DEFAULTCOLOR,
            ::GetSystemMetrics(SM_CXICON), ::GetSystemMetrics(SM_CYICON));
        SetIcon(hIcon, TRUE);
        
        // 小さいアイコン設定
        HICON hIconSmall = AtlLoadIconImage(IDR_MAINFRAME, LR_DEFAULTCOLOR,
            ::GetSystemMetrics(SM_CXSMICON), ::GetSystemMetrics(SM_CYSMICON));
        SetIcon(hIconSmall, FALSE);

        // コントロール設定
        m_button.SubclassWindow(GetDlgItem(IDC_BUTTON_THEME));

        return TRUE;
    }

    void OnOK(UINT uNotifyCode, int nID, CWindow wndCtl){
        EndDialog(nID);
    }

    void OnCancel(UINT uNotifyCode, int nID, CWindow wndCtl){
        EndDialog(nID);
    }
};
			

// SampleProject.cpp
#include "stdafx.h"
#include "resource.h"
#include "ThemeButton.h"
#include "MainDlg.h"

CAppModule _Module;

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

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

    AtlInitCommonControls(ICC_BAR_CLASSES);

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

    CMainDlg dlg;
    int nRet = (int) dlg.DoModal();

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

    return nRet;
}
			

 まず、リソースを作成します。ダイアログにボタンコントロールを配置し、 [Caption]と[ID]を次のように設定します。 なお、ボタンコントロールの[プロパティ]では[Owner Draw]を[True]に設定します。

コントロール名 Caption ID
ボタン テーマボタン IDC_BUTTON_THEME

 次に、stdafx.hヘッダではCThemeImplクラステンプレートを使用するためにatltheme.hヘッダをインクルードします。

 CMainDlgクラスでは、まず、CThemeButtonクラスのインスタンスをメンバ変数として宣言します。 これを使うためには、WM_INITDIALOGメッセージハンドラでサブクラス化する必要があります。 メッセージマップにはメッセージリフレクションを利用するためにREFLECT_NOTIFICATIONSマクロを追加します。

 最後に、SampleProject.cppファイルでMainDlg.hヘッダの前にThemeButton.hヘッダをインクルードします。