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ヘッダをインクルードします。
|