代码改变世界

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的状态,操作系统其实定义了多个状态的图片,根据状态不同进行切换而已

image

image

每个控件都有自己的皮肤特性,如上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;

   };

 

这也算是一种办法,如果只想创建部分控件样式又想利用原有的控件样式的话,可以利用此法,记录一下