WTL学习笔记(5)系统皮肤管理
2011-08-16 22:26 Clingingboy 阅读(2565) 评论(0) 编辑 收藏 举报
首先下载一个Demo看一下效果
http://www.codeproject.com/KB/winsdk/xpvisualstyle.aspx
概念:每个控件都有Part和State的概念,这个同在wpf和silverlight的visual state的概念是相同的。只不过silverlight可以手写xaml,而传统win32只能函数获取样式
如checkbox的状态,操作系统其实定义了多个状态的图片,根据状态不同进行切换而已
每个控件都有自己的皮肤特性,如上checkbox各种状态的样式,可借助DrawThemeBackground等方法将控件部分状态样式输出
wtl的CTheme和CThemeImpl对内置的win32相关的theme函数进行了封装
CTheme属于原生封装,CThemeImpl则是进行了加工.使用起来更加方便.
自定义控件下Theme的使用
如金山开源控件库中,虽然重绘了button等控件,但并非全部控件重新定义.如radiobutton和checkbox就可以使用内置的控件样式.为了不使用内置的创建方法创建这些控件,那么就可以基于自己的架构之上,再结合Theme相关函数绘制需要的控件.
示例
1.CBkWinTheme的封装
该类是金山卫士下的一个文件bkwin\bktheme.h
其功能同wtl的CTheme,对局部的几个重要的函数进行了轻量封装
需要注意的是使用uxtheme.dll,是以载入dll的方式来进行的,最常用的方法莫过于DrawThemeBackground
//////////////////////////////////////////////////////////////////////////
// Class Name: BkTheme
// Description: Windows Theme(XP later)
// Creator: ZhangXiaoxuan
// Version: 2009.5.12 - 1.0 - Create
//////////////////////////////////////////////////////////////////////////
#pragma once
#include <atlcoll.h>
#include <Uxtheme.h>
#include <tmschema.h>
class BkWinThemeFunc
{
typedef HTHEME (WINAPI *FnOpenThemeData)(HWND hwnd, LPCWSTR pszClassList);
typedef HRESULT (WINAPI *FnCloseThemeData)(HTHEME hTheme);
typedef HRESULT (WINAPI *FnDrawThemeBackground)(HTHEME hTheme, HDC hdc, int iPartId, int iStateId, const RECT *pRect, OPTIONAL const RECT *pClipRect);
typedef HRESULT (WINAPI *FnSetWindowTheme)(HWND hwnd, LPCWSTR pszSubAppName, LPCWSTR pszSubIdList);
public:
BkWinThemeFunc()
: m_bThemeValid(FALSE)
, m_pfnOpenThemeData(NULL)
, m_pfnCloseThemeData(NULL)
, m_pfnDrawThemeBackground(NULL)
, m_pfnSetWindowTheme(NULL)
{
HMODULE hModTheme = ::GetModuleHandle(_T("uxtheme.dll"));
if (!hModTheme)
return;
m_bThemeValid = TRUE;
m_pfnOpenThemeData = (FnOpenThemeData)::GetProcAddress(hModTheme, "OpenThemeData");
m_pfnCloseThemeData = (FnCloseThemeData)::GetProcAddress(hModTheme, "CloseThemeData");
m_pfnDrawThemeBackground = (FnDrawThemeBackground)::GetProcAddress(hModTheme, "DrawThemeBackground");
m_pfnSetWindowTheme = (FnSetWindowTheme)::GetProcAddress(hModTheme, "SetWindowTheme");
}
static BOOL IsValid()
{
return _Instance()->m_bThemeValid;
}
static HTHEME OpenThemeData(HWND hwnd, LPCWSTR pszClassList)
{
if (_Instance()->m_pfnOpenThemeData)
{
return _Instance()->m_pfnOpenThemeData(hwnd, pszClassList);
}
else
return NULL;
}
static HRESULT CloseThemeData(HTHEME hTheme)
{
if (_Instance()->m_pfnCloseThemeData)
{
return _Instance()->m_pfnCloseThemeData(hTheme);
}
else
return E_NOTIMPL;
}
static HRESULT DrawThemeBackground(HTHEME hTheme, HDC hdc, int iPartId, int iStateId, const RECT *pRect, OPTIONAL const RECT *pClipRect)
{
if (_Instance()->m_pfnDrawThemeBackground)
{
return _Instance()->m_pfnDrawThemeBackground(hTheme, hdc, iPartId, iStateId, pRect, pClipRect);
}
else
return E_NOTIMPL;
}
static HRESULT SetWindowTheme(HWND hwnd, LPCWSTR pszSubAppName, LPCWSTR pszSubIdList)
{
if (_Instance()->m_pfnSetWindowTheme)
{
return _Instance()->m_pfnSetWindowTheme(hwnd, pszSubAppName, pszSubIdList);
}
else
return E_NOTIMPL;
}
enum
{
BkThemeNameButton = 0,
BkThemeNameEnd,
};
static LPCWSTR ThemeName(int nID)
{
static const LPCWSTR Names[] =
{
L"BUTTON",
L"",
};
static int NamesCount = sizeof(Names) / sizeof(LPCWSTR) - 1;
if (nID >= 0 && nID < NamesCount)
return Names[BkThemeNameButton];
else
return NULL;
}
private:
BOOL m_bThemeValid;
FnOpenThemeData m_pfnOpenThemeData;
FnCloseThemeData m_pfnCloseThemeData;
FnDrawThemeBackground m_pfnDrawThemeBackground;
FnSetWindowTheme m_pfnSetWindowTheme;
static BkWinThemeFunc* ms_pInstance;
static BkWinThemeFunc* _Instance()
{
if (!ms_pInstance)
ms_pInstance = new BkWinThemeFunc;
return ms_pInstance;
}
// static BkWinThemeFunc& _Instance()
// {
// static BkWinThemeFunc s_obj;
//
// return s_obj;
// }
};
__declspec(selectany) BkWinThemeFunc* BkWinThemeFunc::ms_pInstance = NULL;
template<int t_nThemeId, int t_partid>
class CBkWinTheme
{
public:
CBkWinTheme(HWND hWnd = NULL)
: m_hTheme(NULL)
{
if (hWnd)
OpenTheme(hWnd);
}
~CBkWinTheme()
{
BkWinThemeFunc::CloseThemeData(m_hTheme);
}
BOOL IsValid()
{
return (NULL != m_hTheme);
}
BOOL OpenTheme(HWND hWnd)
{
if (m_hTheme)
return FALSE;
m_hTheme = BkWinThemeFunc::OpenThemeData(NULL, BkWinThemeFunc::ThemeName(t_nThemeId));
if (m_hTheme)
return TRUE;
return FALSE;
}
void DrawBackground(HDC hdc, int iStateId, const RECT *pRect)
{
BkWinThemeFunc::DrawThemeBackground(m_hTheme, hdc, t_partid, iStateId, pRect, NULL);
}
protected:
HTHEME m_hTheme;
};
typedef CBkWinTheme<BkWinThemeFunc::BkThemeNameButton, BP_CHECKBOX> CBkCheckBoxTheme;
typedef CBkWinTheme<BkWinThemeFunc::BkThemeNameButton, BP_RADIOBUTTON> CBkRadioBoxTheme;
2.checkbox和radiobox的实现
CBkWindow 之前有讲到过,这里只贴相关OnPaint方法,请先忽略其他,只注意theme的DrawBackground方法,刚好可以用到此处.
class CBkCheckBox : public CBkWindow
{
BKOBJ_DECLARE_CLASS_NAME(CBkCheckBox, "check")
public:
CBkCheckBox()
: m_theme()
{
}
void OnPaint(CDCHandle dc)
{
CRect rcCheckBox = m_rcWindow;
rcCheckBox.right = rcCheckBox.left + CheckBoxSize;
m_theme.OpenTheme(m_hWndContainer);
if (m_theme.IsValid())
{
m_theme.DrawBackground(dc, _GetDrawState(), rcCheckBox);
}
else
{
dc.DrawFrameControl(rcCheckBox, DFC_BUTTON, _GetDrawState());
}
m_rcWindow.left += CheckBoxSize + CheckBoxSpacing;
m_style.m_nTextAlign &= ~DT_BOTTOM;
m_style.m_nTextAlign |= DT_VCENTER;
__super::OnPaint(dc);
m_rcWindow.left -= CheckBoxSize + CheckBoxSpacing;
}
protected:
CBkCheckBoxTheme m_theme;
};
class CBkRadioBox : public CBkWindow
{
BKOBJ_DECLARE_CLASS_NAME(CBkRadioBox, "radio")
public:
CBkRadioBox()
: m_theme()
{
}
virtual BOOL Load(TiXmlElement* pTiXmlElem)
{
if (!CBkWindow::Load(pTiXmlElem))
return FALSE;
return BkWnds::RegisterRadioGroup(this, m_strGroup);;
}
void OnPaint(CDCHandle dc)
{
m_theme.OpenTheme(m_hWndContainer);
if (m_theme.IsValid())
{
m_theme.DrawBackground(dc, _GetDrawState(), rcCheckBox);
}
else
{
dc.DrawFrameControl(rcCheckBox, DFC_BUTTON, _GetDrawState());
}
__super::OnPaint(dc);
}
protected:
CBkRadioBoxTheme m_theme;
};
这也算是一种办法,如果只想创建部分控件样式又想利用原有的控件样式的话,可以利用此法,记录一下