Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C++

Have a glass with WTL!

4.99/5 (26 votes)
5 Jun 2007CPL5 min read 1   3.6K  
Enable Aero glass with your native (WTL) applications and keep backwards compatibility

How do you like it?

Introduction

Sorry, nothing to drink in here.

But if you like the Vista Aero glass effect and wish to enable it in your (existing or new) WTL applications, this article is intended to (and hopefully will) help you.

The native APIs can easily be put at work and the aero namespace allows simple adaptation of existing software.

This article is made up of two parts:

  1. Guided tour is a walk around some of the WTL::aero:: classes usage and resulting effects
  2. Inside the WTL::aero namespace is a more detailed description of the toolset

References

The main documentation about the native APIs used here is the MSDN Desktop Window Manager section.

You can find in CodeProject Michael Dunn's article : Vista Goodies in C++: Using Glass in Your UI, and in Kenny Kerr's blog The Desktop Window Manager and Controls and the Desktop Window Manager.

While I was preparing this article a MFC cousin article Adding or Retrofitting Aero Glass into Legacy Windows Applications was published by Stefan Kuhr.

Prerequisites

To follow this article you need to have correctly installed on your computer:

Obviously you should test on a Vista machine, but you can compile and run everything under XP SP2.

Guided tour

For any aero enabled application

Using the WTL AppWizard create a simple FirstTest application: just click next on the first wizard screen, uncheck the 'Use view' box and click Finish. Now make some simple changes, these steps are always to be done:

  1. Change the constants in stdafx.h:

    C++
    // stdafx.h : include file for the standard system include files,
    //  or project specific include files that are used frequently, but
    //      are changed infrequently
    
    #pragma once
    
    // Change these values to use different versions
    #define WINVER           0x0600
    #define _WIN32_WINNT     0x0600
    #define _WIN32_IE        0x0700
    #define _RICHEDIT_VER    0x0200
    // ...

  2. Extract toolbar.bmp from WtlAero.zip into your project folder FirstTest\res\ .
  3. Extract WtlAero.h from WtlAero.zip into your project folder FirstTest\ and change FirstTest.cpp to include it:

    C++
    // FirstTest.cpp : main source file for FirstTest.exe
    
    #include "stdafx.h"
    
    #include<atlframe.h>
    #include <atlctrls.h>
    #include <atldlgs.h>
    #include <atlctrlw.h>
    
    #include "WtlAero.h"
    
    #include "resource.h"
    
    // ...
  4. In MainFrm.h change the definitions of CFrameWindowImpl and m_CmdBar to their aero:: counterpart:

    C++
    // MainFrm.h : interface of the CMainFrame class
    //
    ////////////////////////////////////////////////////////////////
    #pragma once
    
    class CMainFrame : public aero::CFrameWindowImpl<CMainFrame>,
        public CUpdateUI<CMainFrame>,
        public CMessageFilter, public CIdleHandler
    {
    public:
        DECLARE_FRAME_WND_CLASS(NULL, IDR_MAINFRAME)
    
        CFirstTestView m_view;
    
        aero::CCommandBarCtrl m_CmdBar;
    
    // ...

  5. Change the base class definition in the message map, and move it BEFORE the OnCreate() handler:
    C++
    BEGIN_MSG_MAP(CMainFrame)
     CHAIN_MSG_MAP(aero::CFrameWindowImpl<CMainFrame>)
    MESSAGE_HANDLER(WM_CREATE, OnCreate)
     MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
     COMMAND_ID_HANDLER(ID_APP_EXIT, OnFileExit)
     COMMAND_ID_HANDLER(ID_FILE_NEW, OnFileNew)
     COMMAND_ID_HANDLER(ID_VIEW_TOOLBAR, OnViewToolBar)
     COMMAND_ID_HANDLER(ID_VIEW_STATUS_BAR, OnViewStatusBar)
     COMMAND_ID_HANDLER(ID_APP_ABOUT, OnAppAbout)
     CHAIN_MSG_MAP(CUpdateUI<CMainFrame>)
    END_MSG_MAP()

  6. If you want to run the application under XP or W2k set delay loading for uxtheme.dll and dwmapi.dll:

    Screenshot - Delayload.jpg

  7. That's all. Compile and run. You should get this.

    Screenshot - FirstTest.jpg

Let's do a little more with this first test:

  • To change the toolbar to a transparent one just replace in CMainFrame::OnCreate() the call to CreateSimpleToolBarCtrl():
    C++
    HWND hWndToolBar = CreateAeroToolBarCtrl(m_hWnd,
      IDR_MAINFRAME, FALSE, ATL_SIMPLE_TOOLBAR_PANE_STYLE);

    Screenshot - FirstTestTB.jpg

  • To do some drawing, insert this member in CMainFrame:

    C++
    void Paint(CDCHandle dc, RECT& rClient, RECT& rView, RECT& rDest)
    {
        CLogFont lf;
    
        GetThemeSysFont(TMT_MSGBOXFONT, &lf);
        lf.lfHeight *= 3;
        CFont f = lf.CreateFontIndirect();
    
        HFONT hfOld = dc.SelectFont(f);
    
    
        DrawText(dc, L"Hello Aero", &rClient, DT_CENTER | DT_VCENTER |
            DT_SINGLELINE);
        dc.SelectFont(hfOld);
     }

    Screenshot - FirstTestHello.jpg

To get an Aero About dialog

  1. In the resource editor, open the IDD_ABOUTBOX dialog and change the icon and text control IDs to IDC_APPICON and IDC_APPTEXT
  2. Change the code in aboutdlg.h:

    C++
    // aboutdlg.h : interface of the CAboutDlg class
    //...
    class CAboutDlg : public aero::CDialogImpl<CAboutDlg >
    {
    public:
        enum { IDD = IDD_ABOUTBOX };
    
        BEGIN_MSG_MAP(CAboutDlg)
            CHAIN_MSG_MAP(aero::CDialogImpl<CAboutDlg >)
            MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog)
            COMMAND_ID_HANDLER(IDOK, OnCloseCmd)
            COMMAND_ID_HANDLER(IDCANCEL, OnCloseCmd)
        END_MSG_MAP()
    
    //...
        LRESULT OnInitDialog(UINT /*uMsg*/, WPARAM /*wParam*/,
            LPARAM /*lParam*/, BOOL& /*bHandled*/)
        {
            CenterWindow(GetParent());
            AERO_CONTROL(CStatic, m_Icon, IDC_APPICON)
            AERO_CONTROL(CStatic, m_Text, IDC_APPTEXT)
            AERO_CONTROL(CButton, m_OK, IDOK)
            return TRUE;
        }
    //...

    Screenshot - FirstTestAbout.jpg

AeroSamples

In the AeroSamples project, you can find other aero adapted AppWizard applications:

  • AeroFrame is the same as your FirstTest
  • AeroView uses a simple WTL view class. The changes are the same for CMainFrame and CAboutDlg, CAeroView::Paint() can also work in unthemed runtime environments

    C++
    // AeroView.h : interface of the CAeroView class
    //
    //////////////////////////////////////////////////////////////
    class CAeroView : public aero::CWindowImpl<CAeroView>
    {
    public:
        DECLARE_WND_CLASS(NULL)
    
        BOOL PreTranslateMessage(MSG* pMsg)
        {
            pMsg;
            return FALSE;
        }
    
        void Paint(CDCHandle dc, RECT& rClient, RECT& rView, RECT& rDest)
        {
            CLogFont lf;
    
            if (IsTheming())
            {
                GetThemeSysFont(TMT_MSGBOXFONT, &lf);
                if (!aero::IsSupported())
                    DrawThemeBackground(dc, 1, 1, &rClient, &rDest);
            }
            else
            {
                NONCLIENTMETRICS ncm = { sizeof(NONCLIENTMETRICS) };
                SystemParametersInfo ( SPI_GETNONCLIENTMETRICS,
                    sizeof(NONCLIENTMETRICS), &ncm, false );
                lf = ncm.lfMessageFont;
                dc.FillSolidRect(&rClient, GetSysColor(COLOR_WINDOW));
            }
    
            lf.lfHeight *= 3;
            CFont f = lf.CreateFontIndirect();
            HFONT hfOld = dc.SelectFont(f);
            DrawText(dc, L"Hello Aero", &rClient,
               DT_CENTER | DT_VCENTER | DT_SINGLELINE);
            dc.SelectFont(hfOld);
        }
    
        BEGIN_MSG_MAP(CAeroView)
            CHAIN_MSG_MAP(aero::CWindowImpl<CAeroView>)
        END_MSG_MAP()
    };

  • AeroTab is an "AppWizard Tab View Application" using the same CAeroView class where CTabView is replaced by aero::CTabView.

    Screenshot - AeroTab.jpg

  • AeroSplit is an "AppWizard Explorer Application" with a few more changes from the Wizard generated code in MainFrm.h

    C++
    class CMainFrame : public aero::CFrameWindowImpl<CMainFrame>,
       public CUpdateUI<CMainFrame>,
       public CMessageFilter, public CIdleHandler
    {
    public:
        DECLARE_FRAME_WND_CLASS(NULL, IDR_MAINFRAME)
    
        aero::CSplitterWindow m_splitter;
        aero::CPaneContainer m_pane;
        aero::CCtrl<CTreeViewCtrl> m_treeview;
        CAeroView m_view;
        aero::CCommandBarCtrl m_CmdBar;
    //...
        LRESULT OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/,
            BOOL& /*bHandled*/)
        {
    //...
          m_treeview.SetWindowTheme(L"explorer", NULL);
          HTREEITEM hti = m_treeview.InsertItem(L"Test", TVI_ROOT, TVI_LAST);
          m_treeview.InsertItem(L"Child Test", hti, TVI_LAST);
    //...

    Screenshot - AeroSplit.jpg

  • AeroDialog is a "modal dialog AppWizard application" with some controls added in the dialog editor

    C++
    // MainDlg.h : interface of the CMainDlg class
    //
    //////////////////////////////////////////////////////////////////////
    
    #pragma once
    
    class CMainDlg : public aero::CDialogImpl<CMainDlg>
    {
    public:
        enum { IDD = IDD_MAINDLG };
    
        BEGIN_MSG_MAP(CMainDlg)
            CHAIN_MSG_MAP(aero::CDialogImpl<CMainDlg>)
    //...
        LRESULT OnInitDialog(UINT /*uMsg*/, WPARAM /*wParam*/, 
                    LPARAM /*lParam*/, BOOL& /*bHandled*/)
        {
        //...
            AERO_CONTROL(CButton, m_OK, IDOK)
            AERO_CONTROL(CButton, m_Cancel, IDCANCEL)
            AERO_CONTROL(CButton, m_About, ID_APP_ABOUT)
            AERO_CONTROL(CEdit, m_Edit, IDC_EDIT1)
            AERO_CONTROL(CButton, m_C1, IDC_CHECK1)
            AERO_CONTROL(CButton, m_C2, IDC_CHECK2)
            AERO_CONTROL(CButton, m_R1, IDC_RADIO1)
            AERO_CONTROL(CButton, m_R2, IDC_RADIO2)
            AERO_CONTROL(CButton, m_Group, IDC_GROUP)
            AERO_CONTROL(CListBox, m_LB, IDC_LIST1)
            AERO_CONTROL(CComboBox, m_CB, IDC_COMBO1)
    
            m_LB.AddString(L"TEST Item");
            m_CB.AddString(L"Combo Test");
    
            SetOpaqueUnder(IDC_MONTHCALENDAR1);
    
            return TRUE;
        }
    //...

    Sorry, it's partly in french :)

Inside the WTL::aero namespace

The WTL::aero namespace provides the toolset enabling any WTL application for aero glass.

Namespace functions

These functions perform the runtime tests allowing backwards compatibility.

  • bool aero::IsSupported(): if this is false don't try anything
  • bool aero::IsComposing(): if this is true go ahead
  • bool aero::IsOpaqueBlend(): if this is true Vista transparency is off
  • template <class TCtrl> BOOL aero::Subclass(TCtrl& Ctrl, HWND hCtrl) subclasses the hCtrl control with the TCtrl type Ctrl only when aero::IsSupported()

The base classes

The base classes are the engine of the library.

  • aero::CAeroBase gives it's derived classes access to the Theme API. The Subclass() member accepts a control ID and calls aero::Subclass() on it's HWND. The various DrawText() members use ::DrawThemeTextEx() when drawing on translucent background.

    C++
    ///////////////////////////////////////////////////////////////////////
    // aero::CAeroBase - Base class for Aero translucency (when available)
    
    template <class T>
    class CAeroBase :
        public WTL::CThemeImpl<T>
    {
    public:
        CAeroBase(LPCWSTR lpstrThemeClassList = L"globals")
        {
            SetThemeClassList(lpstrThemeClassList);
        }
    
        bool IsTheming() const
        {
            return m_hTheme != 0;
        }
    
        template <class TCtrl>
        BOOL Subclass(TCtrl& Ctrl, INT idCtrl)
        {
            return aero::Subclass(Ctrl, 
                static_cast<T*>(this)->GetDlgItem(idCtrl));
        }
    
        bool DrawPartText(HDC dc, int iPartID, int iStateID,
            LPCTSTR pStr, LPRECT prText, UINT uFormat, DTTOPTS &dto)
        {
            HRESULT hr = S_FALSE;
            if(IsTheming())
                if (IsSupported())
                    hr = DrawThemeTextEx (dc, iPartID, iStateID,
                        pStr, -1, uFormat, prText, &dto );
                else
                    hr = DrawThemeText(dc, iPartID, iStateID, pStr, -1,
                        uFormat, 0, prText);
            else
                hr = CDCHandle(dc).DrawText(pStr, -1, prText, uFormat) != 0 ?
                     S_OK : S_FALSE;
    
            return SUCCEEDED(hr);
        }
    
         bool DrawPartText(HDC dc, int iPartID, int iStateID, LPCTSTR pStr,
            LPRECT prText, UINT uFormat,
            DWORD dwFlags = DTT_COMPOSITED, int iGlowSize = 0)
        {
            DTTOPTS dto = {sizeof(DTTOPTS)};
            dto.dwFlags = dwFlags;
            dto.iGlowSize = iGlowSize;
            return DrawPartText(dc, iPartID, iStateID, pStr, prText, 
                                uFormat, dto);
        }
    
        bool DrawText(HDC dc, LPCTSTR pStr, LPRECT prText, UINT uFormat,
            DTTOPTS &dto)
        {
            return DrawPartText(dc, 1, 1, pStr, prText, uFormat, dto);
        }
    
        bool DrawText(HDC dc, LPCTSTR pStr, LPRECT prText, UINT uFormat,
            DWORD dwFlags = DTT_COMPOSITED, int iGlowSize = 0)
        {
            return DrawPartText(dc, 1, 1, pStr, prText, uFormat, dwFlags,
                iGlowSize);
        }
    };
  • aero::CAeroImpl derives from CAeroBase and WTL::CBufferedPaintImpl and manages the translucent area through it's m_Margins member.
    The SetMargins() and various SetOpaque() members allow control of the translucent area.
    The DoPaint() member is called by CBufferedPaintImpl::OnPaint() with a system buffered DC.
    In turn it calls the derived class Paint(CDCHandle dc, RECT& rClient, RECT& rView, RECT& rDest) where rClient is the Windows client rectangle, rView is the area inside the margins, rDest is the painted area.

    C++
    /////////////////////////////////////////////////////////////////////
    // aero::CAeroImpl - implementation of Aero translucency (when available)
    
    template <class T>
    class CAeroImpl :
        public WTL::CBufferedPaintImpl<T>,
        public CAeroBase<T>
    {
    public:
        CAeroImpl(LPCWSTR lpstrThemeClassList = L"globals") :
            CAeroBase<T>(lpstrThemeClassList)
        {
            m_PaintParams.dwFlags = BPPF_ERASE;
            MARGINS m = {-1};
            m_Margins = m;
        }
    
        MARGINS m_Margins;
    
        bool SetMargins(MARGINS& m)
        {
            m_Margins = m;
            T* pT = static_cast<T*>(this);
            return pT->IsWindow() && IsComposing() ?
                SUCCEEDED(DwmExtendFrameIntoClientArea
                        (pT->m_hWnd, &m_Margins)) :
                true;
        }
    
        bool SetOpaque(bool bOpaque = true)
        {
            MARGINS m = {bOpaque - 1};
            return SetMargins(m);
        }
    
        bool SetOpaque(RECT &rOpaque)
        {
            T* pT = static_cast<T*>(this);
            RECT rClient;
            pT->GetClientRect(&rClient);
            MARGINS m = {rOpaque.left, rClient.right - rOpaque.right,
                rOpaque.top, rClient.bottom - rOpaque.bottom};
            return SetMargins(m);
        }
    
        bool SetOpaqueUnder(ATL::CWindow wChild)
        {
            T* pT = static_cast<T*>(this);
            ATLASSERT(wChild.IsWindow());
            ATLASSERT(pT->IsChild(wChild));
    
            RECT rChild;
            wChild.GetWindowRect(&rChild);
            pT->ScreenToClient(&rChild);
    
            return SetOpaque(rChild);
        }
    
        bool SetOpaqueUnder(UINT uID)
        {
            return SetOpaqueUnder(static_cast<T*>(this)->GetDlgItem(uID));
        }
    
    // implementation
        void DoPaint(CDCHandle dc, RECT& rDest)
        {
            T* pT = static_cast<T*>(this);
    
            RECT rClient;
            pT->GetClientRect(&rClient);
    
            RECT rView = {rClient.left + m_Margins.cxLeftWidth,
                rClient.top + m_Margins.cyTopHeight,
                rClient.right - m_Margins.cxRightWidth,
                rClient.bottom - m_Margins.cyBottomHeight};
    
            if (!IsComposing())
                if (IsTheming())
                    pT->DrawThemeBackground(dc, WP_FRAMEBOTTOM,
                    pT->m_hWnd == GetFocus() ? FS_ACTIVE : FS_INACTIVE,
                    &rClient, &rDest);
                else
                    dc.FillSolidRect(&rClient, ::GetSysColor(COLOR_MENUBAR));
    
            if ((m_Margins.cxLeftWidth != -1) && !::IsRectEmpty(&rView))
            {
                dc.FillSolidRect(&rView, ::GetSysColor(COLOR_WINDOW));
                if (!m_BufferedPaint.IsNull())
                    m_BufferedPaint.MakeOpaque(&rView);
            }
            else
                ::SetRectEmpty(&rView);
    
            pT->Paint(dc, rClient, rView, rDest);
        }
    
        // Overrideables
        void Paint(CDCHandle /*dc*/, RECT& /*rClient*/,
            RECT& /*rView*/, RECT& /*rDest*/)
        {}
    
        void OnComposition()
        {}
    
        void OnColorization()
        {}
    
        BEGIN_MSG_MAP(CAeroImpl)
            CHAIN_MSG_MAP(CThemeImpl<T>)
            MESSAGE_HANDLER(WM_CREATE, OnCreate)
            MESSAGE_HANDLER(WM_ACTIVATE, OnActivate)
            MESSAGE_HANDLER(WM_DWMCOMPOSITIONCHANGED, OnCompositionChanged)
            MESSAGE_HANDLER(WM_DWMCOLORIZATIONCOLORCHANGED, 
                            OnColorizationChanged)
            CHAIN_MSG_MAP(CBufferedPaintImpl<T>)
        END_MSG_MAP()
    
        LRESULT OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/,
            LPARAM /*lParam*/, BOOL& bHandled)
        {
            if (IsThemingSupported())
                OpenThemeData();
    
            if (IsComposing())
                ::DwmExtendFrameIntoClientArea(static_cast<T*>(this)->m_hWnd,
                &m_Margins);
            return bHandled = FALSE;
        }
    
        LRESULT OnActivate(UINT /*uMsg*/, WPARAM /*wParam*/,
            LPARAM /*lParam*/, BOOL& bHandled)
        {
            if (!IsComposing() && IsTheming())
                static_cast<T*>(this)->Invalidate(FALSE);
            return bHandled = FALSE;
        }
    
        LRESULT OnCompositionChanged(UINT /*uMsg*/, WPARAM /*wParam*/,
            LPARAM /*lParam*/, BOOL& bHandled)
        {
            if (IsComposing())
                SetMargins(m_Margins);
            static_cast<T*>(this)->OnComposition();
            return bHandled = FALSE;
        }
    
        LRESULT OnColorizationChanged(UINT /*uMsg*/, WPARAM /*wParam*/,
            LPARAM /*lParam*/, BOOL& bHandled)
        {
            static_cast<T*>(this)->OnColorization();
            return bHandled = FALSE;
        }
    };

The adapter classes

These classes apply the aero glass effect to existing controls.

  • aero::CCtrl is used to subclass system and common controls.
    Two members are intended for specialization: GetThemeName() returns the control's theme name for opening at creation or subclassing time, CtrlPaint(HDC hdc, RECT& rCtrl, RECT& rPaint) is called for painting through an overriden OnBufferedPaint() member.
    The three DrawCtrlxxx() members are helpers for the specialized drawing routines.
    The AERO_CONTROL() macro helps declaring and subclassing child controls in one step.

    C++
    //////////////////////////////////////////////////////////////////////////
    // aero::CCtrl -  implementation of Aero drawing for system controls
    // Note: This class is intended for system themed control specializations
    
    template<class TBase>
    class CCtrl :
        public WTL::CBufferedPaintWindowImpl<CCtrl<TBase>, TBase>,
        public CAeroBase<CCtrl<TBase> >
    {
    public:
    
        typedef CAeroBase<CCtrl<TBase> > baseAero;
        typedef WTL::CBufferedPaintWindowImpl<CCtrl<TBase>, TBase> baseWindow;
    
        DECLARE_WND_SUPERCLASS(NULL, TBase::GetWndClassName())
    
        // creation and initialization
        CCtrl(LPCWSTR lpstrThemeClassList = GetThemeName()) :
            baseAero(lpstrThemeClassList)
        {
            m_PaintParams.dwFlags = BPPF_ERASE;
        }
    
        CCtrl<TBase>& operator =(HWND hWnd)
        {
            TBase::m_hWnd = hWnd;
            return *this;
        }
    
        HWND Create(HWND hWndParent, ATL::_U_RECT rect = NULL,
            LPCTSTR szWindowName = NULL,
            DWORD dwStyle = 0, DWORD dwExStyle = 0,
            ATL::_U_MENUorID MenuOrID = 0U, LPVOID lpCreateParam = NULL)
        {
            TBase baseCtrl;
            if (baseCtrl.Create(hWndParent, rect.m_lpRect,
                szWindowName, dwStyle, dwExStyle, 
                        MenuOrID.m_hMenu, lpCreateParam)
                != NULL)
                SubclassWindow(baseCtrl.m_hWnd);
            return m_hWnd;
        }
    
        BOOL SubclassWindow(HWND hWnd)
        {
            ATLASSERT(IsSupported());
            if(baseWindow::SubclassWindow(hWnd))
                OpenThemeData();
            return m_hWnd != NULL;
        }
    
        // specializables
        static LPCWSTR GetThemeName()
        {
            return TBase::GetWndClassName();
        }
    
        void CtrlPaint(HDC hdc, RECT& /*rCtrl*/, RECT& rPaint)
        {
            DefCtrlPaint(hdc, rPaint);
        }
    
        // operations
        void DefCtrlPaint(HDC hdc, RECT& rPaint, bool bEraseBkGnd = false)
        {
            if (bEraseBkGnd)
                DefWindowProc(WM_ERASEBKGND, (WPARAM)hdc, NULL);
            DefWindowProc(WM_PAINT, (WPARAM)hdc, 0);
            m_BufferedPaint.MakeOpaque(&rPaint);
        }
    
        BOOL DrawCtrlBackground(HDC hdc, int nPartID, int nStateID,
            RECT &rCtrl, RECT &rPaint)
        {
            return SUCCEEDED(DrawThemeBackground(hdc, nPartID, nStateID,
                &rCtrl, &rPaint));
        }
    
        BOOL DrawCtrlEdge(HDC hdc, int nPartID, int nStateID, RECT &rCtrl,
            UINT uEdge = EDGE_ETCHED, UINT uFlags = BF_RECT,
            LPRECT pContentRect = NULL)
        {
            return SUCCEEDED(DrawThemeEdge(hdc, nPartID, nStateID, &rCtrl,
                uEdge, uFlags, pContentRect));
        }
    
        BOOL DrawCtrlText(CDCHandle dc, int nPartID, int nStateID, 
            UINT uFormat, RECT &rCtrl, HFONT hFont = 0,
            DWORD dwFlags = DTT_COMPOSITED, int iGlowSize = 0)
        {
            HRESULT hr;
            RECT rText;
            hr = GetThemeBackgroundContentRect
            (dc, nPartID, nStateID, &rCtrl, &rText);
            MARGINS m = {0};
            hr = GetThemeMargins(dc, nPartID, nStateID, TMT_CONTENTMARGINS,
                &rText, &m);
            rText.left += m.cxLeftWidth;
            rText.right -= m.cxRightWidth;
            int iLength = GetWindowTextLength();
            if (iLength > 0)
            {
                CTempBuffer<wchar /> sText(++iLength);
                GetWindowText(sText, iLength);
    
                HFONT hf = dc.SelectFont(hFont == 0 ? GetFont() : hFont);
                hr = DrawPartText(dc, nPartID, nStateID, sText,  &rText ,
                    uFormat, dwFlags, iGlowSize);
                dc.SelectFont(hf);
            }
            return SUCCEEDED(hr) && iLength > 0;
        }
    
        // implementation
        void DoBufferedPaint(HDC hdc, RECT& rPaint)
        {
            HDC hDCPaint = NULL;
            RECT rCtrl;
            GetClientRect(&rCtrl);
            m_BufferedPaint.Begin(hdc, &rCtrl, m_dwFormat, &m_PaintParams,
                &hDCPaint);
            ATLASSERT(hDCPaint != NULL);
            CtrlPaint(hDCPaint, rCtrl, rPaint);
            m_BufferedPaint.End();
        }
    
        void DoPaint(HDC /*hdc*/, RECT& /*rCtrl*/)
        {
            DefWindowProc();
        }
    
        BEGIN_MSG_MAP(CCtrl)
            MESSAGE_HANDLER(WM_PAINT, OnPaintMsg)
            MESSAGE_HANDLER(WM_ERASEBKGND, OnPaintMsg)
            CHAIN_MSG_MAP(baseAero)
            CHAIN_MSG_MAP(baseWindow)
        END_MSG_MAP()
    
        LRESULT OnPaintMsg(UINT /*uMsg*/, WPARAM /*wParam*/, 
        LPARAM /*lParam*/, BOOL& bHandled)
        {
            if(!IsComposing())
                return DefWindowProc();
            else
                return bHandled = FALSE;
        }
    };
    
    /////////////////////////////////////////////////////////////////////////
    // Macro declaring and subclassing a control
    // (static declaration to use in OnCreate or OnInitDialog members)
    
    #define AERO_CONTROL(type, name, id)\
        static aero::type name;\
        Subclass(name, id);

  • aero::CCtrlImpl enables Aero drawing on top of an existing TCtrlImpl. The DoPaint() member may be overridden in derived classes to call TCtrlImpl relevant code.

    C++
    //////////////////////////////////////////////////////////////////////////
    // aero::CCtrlImpl -  implementation of Aero drawing for user and WTL 
    // defined controls
    // Note: This class is intended for derivation
    
    template <class T, class TCtrlImpl, bool t_bOpaque = false>
    class ATL_NO_VTABLE CCtrlImpl :
        public TCtrlImpl,
        public CAeroImpl<T>
    {
    public:
        DECLARE_WND_SUPERCLASS(NULL, TCtrlImpl::GetWndClassName())
    
        CCtrlImpl(LPCWSTR lpstrThemeClassList = L"window") : 
                            CAeroImpl(lpstrThemeClassList)
        {
            m_PaintParams.dwFlags = BPPF_ERASE;
        }
    
        void DoPaint(HDC hdc, RECT& rect)
        {
            BOOL bHandled = TRUE;
            TCtrlImpl::OnPaint(WM_PAINT, (WPARAM) hdc, NULL, bHandled);
            if (t_bOpaque)
                m_BufferedPaint.MakeOpaque(&rect);
        }
    
        BEGIN_MSG_MAP(CCtrlImpl)
            CHAIN_MSG_MAP(CAeroImpl<T>)
            CHAIN_MSG_MAP(TCtrlImpl)
        END_MSG_MAP()
    
    };

The replacement classes

These are Aero enabled classes that replace the WTL:: or ATL:: classes with same name. They are used in the first part guided tour.

Derivable classes: these classes derive from aero::CAeroImpl:

  • aero::CWindowImpl< T, TBase, TWinTraits >
  • aero::CDialogImpl<T, TBase>
  • aero::CFrameWindowImpl<T, TBase, TWinTraits>
  • aero::CPropertySheetImpl<T, TBase>
  • aero::CPropertyPageImpl<T, TBase>
  • aero::CSplitterImpl<T, t_bVertical>
  • aero::CSplitterWindowImpl<T, t_bVertical, TBase, TWinTraits>
  • aero::CSplitterWindowT<t_bVertical>

Classes derived or specialized from aero::CCtrl:

  • aero::CEdit
  • aero::CTabCtrl
  • aero::CToolBarCtrl
  • aero::CStatusBarCtrl
  • aero::CListBox
  • aero::CComboBox
  • aero::CStatic
  • aero::CButton

Classes derived from aero::CCtrlImpl:

  • aero::CCommandBarCtrl
  • aero::CTabView
  • aero::CPaneContainer
  • aero::CPrintPreviewWindow

Conclusion

Enabling the nice Aero glass on existing native code applications is not so difficult. Cheers!

Revision history

  • 5.31.2007 Released
  • 6.5.2007 Fixed backwards compatibility issues in aero::CTabView and samples common CAeroView and adjusted the article

License

This article, along with any associated source code and files, is licensed under The Common Public License Version 1.0 (CPL)