XP Theme Support for Rich Edit and Custom Controls

Introduction

The class distributed with this article is intended to be used by any program, developed in C++, that’s using a Rich Edit control. For some reason, Microsoft never updated its Rich Edit library after the release of Windows XP to make the control theme-aware. If your program supports themes (with the use of a manifest, for example) and is displaying a Rich Edit box in a window containing other controls, chances are that you already noticed how bad the Rich Edit looks compared to the other controls. I always thought that newer releases of the Rich Edit library would bring theme support, but because it’s been more than two years already, I decided to create CRichEditThemed to take care of the problem. This little class has been designed to be incorporated in almost any Visual C++ project, painlessly, and necessitates only one function call to make your Rich Edit controls look like any other native control of Windows XP.

It is surprisingly difficult to find the proper documentation for applying a theme to a non-native control. It is true that improving the look of the borders of your edit controls was probably never a top priority in your schedule; however, it doesn’t change the fact that Rich Edit controls look totally out of place in Windows XP and make any program’s GUI lose some of its appeal. Some programs solved the situation by using Rich Edit controls in windowless modes (which is complete overkill if you’re only trying to achieve theme support) whereas some others resorted in using weird GUI tricks that completely break down when users are not using the native theme of Windows XP. I searched a lot for some proper indication about adding real theme support to a control and the only thing I found was replies such as “use DrawThemeEdge,” which, if you’ve been considering the problem for more than 10 minutes, is not very helpful (and wrong anyway). The CRichEditThemed class presented here solves this one and only problem and adds genuine theme support to any Rich Edit controls. I tried to comment the code as much as possible for those who want to understand how it works, and made the usage as simple as possible for those who don’t have more than five minutes to spend on the issue.

Comments, suggestions, and bug reports are always appreciated. I’m not used to sharing code in this manner. However, I do think that nowadays, no programmer should have to be bothered improving the look of a Rich Edit control; you all have more important problems to take care of. As you will see, the class could also be easily modified to support other kinds of controls (including custom controls and ActiveX) that should make this article a good read for anybody interested in using the Windows XP theme library for its own purpose. This class is here to be used, shared, and redistributed.

Using the Class

The CRichEditThemed class was designed from the ground up to to be as simple to use as possible. You won’t need to control the lifetime of a new object, you won’t need to relay window messages, and you won’t need to include another library (such as MFC or ATL) in your project to make the class work (with the exception of STL, which is a standard C++ library, included with any major C++ compiler, and is not supposed to create any conflict with the rest of your program). The class uses TCHAR to ensure Unicode compatibility and is totally backward compatible (meaning that it will stay silent when used in older versions of Windows or in an environment where themes are not enabled). Here are the files that will be automatically included in your project by RichEditThemed.h:

#include <tchar.h>      //Used for ANSI/Unicode compatibility
#include <map>          //Used internally by the class
#include <uxtheme.h>    //Used to access the UxTheme library
#include <tmschema.h>   //Definitions used by some of the UxTheme
                        //library functions

In addition, you must ensure that your Platform SDK is up to date and that _WIN32_WINNT is defined to at least 0x0501. If it isn’t, the compiler won’t give you access to the UxTheme API and you will get a compilation error. This can be done by adding a preprocessor definition in the settings of your projects or by adding the proper #define statement before the first inclusion of windows.h in your project.

//stdafx.h
#define _WIN32_WINNT 0x0501
#include <windows.h>

Every theme-specific function is imported at runtime; this means that your EXE/DLL won’t have any additional external dependency caused by CRichEditThemed. The only files you must include in your project are RichEditThemed.cpp and RichEditThemed.h. The class has only one public function that creates a new object in memory and attaches the specified Rich Edit control to the newly created object. Everything else, including destruction and memory management, is taken care of internally by the class.

public static bool CRichEditThemed::Attach(HWND hRichEdit);

The CRichEditThemed::Attach() function must be called during the creation of the Rich Edit control’s parent window, before the control is displayed onscreen for the first time. This is generally achieved by making the call when processing WM_CREATE or WM_INITDIALOG. The object is not designed to attach on a control that is already displayed onscreen and usable; that’s about the only thing you will have to keep in mind. Here is a code snippet showing how to use the class:

HWND hRichEdit = GetDlgItem(m_hParent, IDC_RICHEDIT);
CRichEditThemed::Attach(hRichEdit);

After CRichEditThemed::Attach() has been called, the Rich Edit control will be subclassed by CRichEditThemed until its destruction. The control is subclassed only and always in Windows XP (or above, as long as the UxTheme.dll library can be found). The control will be subclassed even if themes are not enabled at the time of creation; this allows proper theme support in case of a theme change while the parent window is still open. The class also takes care of redrawing the control after it has been enabled/disabled or when its style changes (to alter the border after creation, for example). The class will only draw the theme if the Rich Edit control has the WS_BORDER style because it supposes that only the edges of the control are themed (which should always be the case for any “editbox” theme that makes sense).

The only known limitation concerns the use of the “Auto HScroll” style in conjunction with both “Horizontal Scroll” and “Vertical Scroll.” When both scrollbars are displayed, the intersection of the two bars is drawn automatically by the control and, as a result, it doesn’t look as good as it would look in a native themed control. Because most programmers prefer not to use “Auto HScroll” with Rich Edit controls displayed in dialog boxes (it often makes the control more difficult to use), this shouldn’t be too much of a problem. (Sorry if it is for you; however, the control will still look much better with the class than without.) Apart from that, you shouldn’t notice any difference between native edit controls and your newly transformed Rich Edit controls. If you do, it’s a bug and it will need to be reported and be fixed.

Dissecting the Class

This section is for curious people who want to know how the class works internally (or, if you’re like me, you just want to check the code yourself before you include it in your software, which is just as justified). The explanations given here are not needed to use the class; however, they should give you enough insights to be able to use the same technique for other kinds of controls. A lot of comments have been added in the code. It should be self explanatory for the most part; however, if you’re not familiar with the concept of themes and non-client areas, this section of the article should fill the gaps. The following tutorial has been written in chronological manner and is meant to be read from beginning to end, every sub-section supposing that the previous section has been read and understood.

Personal note: Subclassing is one of my favourite ways of working (which you already know if you’re a user of Messenger Plus!). Great things can be done in no time with this technique and almost anything in Windows can be shortcut to attain one’s goal. It is thanks to subclassing that CRichEditThemed doesn’t need to be instantiated the “usual way.” Once created, the object monitors the actions of the Rich Edit control it’s been attached to. Because the control is guaranteed to receive a WM_DESTROY message when its parent is destroyed, you know you can count on it to destroy the attached CRichEditThemed object as well. You don’t need to be familiar with subclassing techniques to follow this tutorial; however, you do need to know what a message loop is and how Windows communicates with the many windows/controls created in its environment.

I’ll start the tutorial with the public entry point of the class. As stated previously, the only public method of CRichEditThemed is CRichEditThemed::Attach() and it is responsible for doing two things: First, it tries to import the functions it will require later on from the UxTheme library. If the user is running an older version of Windows, this library will not be available and CRichEditThemed won’t attach itself to the Rich Edit control. Then, after a couple of basic verifications, a new CRichEditThemed object is created:

bool CRichEditThemed::Attach(HWND hRichEdit)
{
   if(IsWindow(hRichEdit))
   {
      //Prevent double subclassing
      if(CRichEditThemed::m_aInstances.find(hRichEdit) ==
         CRichEditThemed::m_aInstances.end())
      {
         //If this function fails, this version of Windows doesn't
         //support themes
         if(InitLibrary())
         {
            //Note: The object will be automatically deleted when
            //the richedit control dies.
            CRichEditThemed *obj = new CRichEditThemed(hRichEdit);
            return true;
         }
      }
   }
   return false;
}

The creation of a new object involves two very distinct operations. First, you need to subclass the Rich Edit control with a call to SetWindowLong(). Subclassing is an operation consisting of replacing the message loop procedure of a window (also called winproc) with a new custom one. The new winproc is responsible for relaying all the messages it receives to the original procedure; this gives the flexibility needed to override any message or silent some of them when needed.

The winproc is a static function, so you need a way to associate the Rich Edit control with its attached CRichEditThemed object. For that purpose, an STL map is used, taking the handle of the control as key and a pointer to the CRichEditThemed object as an element. Because subclassing is a subject on its own, I will not spend more time talking about it. You are encouraged to check how this part of the class works and get yourself a good book on the Windows API if the matter interests you.

The second operation performed by the constructor consists of verifying the current state of the Rich Edit control by calling CRichEditThemed::VerifyThemedBorderState(). This function is in charge of deciding whether or not a themed border should be drawn around the Rich Edit control. It is called every time the status of the control changes.

CRichEditThemed::CRichEditThemed(HWND hRichEdit)
   : m_hRichEdit(hRichEdit), m_bThemedBorder(false)
{
   //Subclass the richedit control; this way, the caller doesn't
   //have to relay the messages by itself
   m_aInstances[hRichEdit] = this;
   m_pOriginalWndProc = (WNDPROC)SetWindowLong(hRichEdit,
                        GWL_WNDPROC,
                        (LONG)&RichEditStyledProc);

   //Check the current state of the richedit control
   ZeroMemory(&m_rcClientPos, sizeof(RECT));
   VerifyThemedBorderState();
}

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read