Introduction
Looking around for a theme-aware toggle button that could display text and an icon,
I could not find one, so
XButtonXP is the result: a pushbutton that can also be a toggle button,
and can display icon and/or text.
What's New in v1.3
In this version I have corrected (I believe) all the outstanding bugs
that have been reported (see list), and added several new features:
XButtonXP in Action
Here is what
XButtonXP looks like with themes:
Icon on Left
|
Icon on Right
|
No Icon
|
No Text
|
Toggled
|
Disabled
|
And here is what XButtonXP looks like without themes:
Icon on Left
|
Icon on Right
|
No Icon
|
No Text
|
Toggled
|
Disabled
|
XButtonXP can also be drawn using toolbar style:
No Hover
|
Hover
|
CXButtonXP Implementation Notes
Theme Support
Normally, when you add
manifest file
to exe, all controls used by exe will be displayed
as themed. However, this is not true of ownerdraw controls - the XP theming engine just ignores them.
Since I needed an ownerdraw button, it meant that
XButtonXP had to handle the XP theming support.
I created the
CXThemeHelper
class to handle
loading
UXTHEME.DLL and getting theme function pointers via
GetProcAddress()
.
To make
CXThemeHelper
lightweight and easy to reuse, I designed it as singleton class, so that there is only one copy of
DLL's module handle and function pointers per process.
The first time that CXButtonXP::DrawItem()
is called, the theme is opened for Button
class:
if (ThemeHelper.IsThemeLibAvailable())
{
m_hTheme = ThemeHelper.OpenThemeData(m_hWnd, _T("Button"));
}
Aside from using theme functions for drawing the controls, one other thing CXButtonXP
must do is handle WM_THEMECHANGED
message, which is
broadcast to every window following a theme change event:
case WM_THEMECHANGED:
{
if (IsThemed())
{
if (m_hTheme)
{
ThemeHelper.CloseThemeData(m_hTheme);
m_hTheme = NULL;
m_bFirstTime = TRUE;
}
}
}
CXThemeHelper API
The
CXThemeHelper
API includes:
- BOOL IsAppThemed()
- BOOL IsThemeActive()
- BOOL IsThemeLibAvailable()
- BOOL CloseThemeData(HTHEME hTheme)
- BOOL DrawThemeBackground()
- BOOL DrawThemeParentBackground()
- BOOL DrawThemeText()
- BOOL GetThemeBackgroundContentRect()
- HTHEME OpenThemeData()
Controlling XButtonXP Theming
CXButtonXP
will use themes to draw button in the following circumstances:
-
The app is running on XP
-
Themes are enabled on system, and app has not been set to
"Disable visual themes" via Properties dialog:
-
Themes have not been disabled via
CXButtonXP::EnableTheming()
function.
CXButtonXP Starting Point
I used Ewan Ward's article
Native Win32 Theme aware Owner-draw Controls without MFC as starting point for
XButtonXP.
First, I adapted his code for MFC, and then used
CXThemeHelper
to enable theme support.
The next step was to fix default button problem.
Default Button Problem
This problem plagues all ownerdraw buttons. Left uncorrected, it can give the visual appearance
that there are multiple default buttons on dialog, and improperly handles ENTER key.
Fortunately,
Paolo Messina describes this problem in detail, and has written very nice
class to transparently fix this problem for any ownerdraw button.
Keyboard Input
The final task was to ensure that SPACE and ENTER keys worked properly.
On standard pushbutton, these keys produce button-click event.
In order for
XButtonXP to handle them in same way, I had to intercept
WM_GETDLGCODE
message, and ask for all keys. At the same
time, I did not want to interfere with TAB key processing. The
WM_GETDLGCODE message provides mechanism to do this, by returning
appropriate code. Then, I added handlers for
WM_KEYDOWN
and
WM_KEYUP
, to catch ENTER and SPACE
key presses. I converted these to
WM_LBUTTONDOWN
and
WM_LBUTTONUP
messages, to emulate mouse click.
CXButtonXP Functions
Here are functions in CXButtonXP
:
Function |
Description |
CXButtonXP& EnableTheming(BOOL bEnable) |
Enable visual themes for button. |
COLORREF GetBackgroundColor() |
Retrieve button custom background color (XBUTTONXP_NO_COLOR if none). |
BOOL GetDrawToolbar() |
Get toolbar style (TRUE = use toolbar style). |
COLORREF GetTextColor() |
Retrieve button custom text color (XBUTTONXP_NO_COLOR if none). |
BOOL GetToggle() |
Get toggle style (TRUE = button acts like toggle). |
BOOL GetToggleState() |
Get toggle state (TRUE = button is pressed). |
BOOL IsThemed() |
Get theming state (TRUE = visual theme is being used). |
CXButtonXP& SetBackgroundColor(COLORREF rgb = XBUTTONXP_NO_COLOR) |
Set button custom background color. Default is XBUTTONXP_NO_COLOR. |
CXButtonXP& SetDrawToolbar(BOOL bDrawToolbar) |
Set toolbar style. Default is FALSE. |
CXButtonXP& SetIcon(UINT nIDResource, ICON_ALIGNMENT ia = LEFT) |
Set icon from resource id. |
CXButtonXP& SetIcon(HICON hIcon, ICON_ALIGNMENT ia = LEFT) |
Set icon from HICON. |
CXButtonXP& SetIconAlignment(ICON_ALIGNMENT ia) |
Set icon alignment on button. Default is LEFT. |
CXButtonXP& SetTextColor(COLORREF rgb = XBUTTONXP_NO_COLOR) |
Set button custom text color. Default is XBUTTONXP_NO_COLOR. |
CXButtonXP& SetToggle(BOOL bIsToggle) |
Set toggle style. Default is FALSE. |
CXButtonXP& SetToggleState(BOOL bToggled) |
Set toggle state. Default is FALSE. |
How to use
To integrate CXButtonXP
into your app, you first need to add following files to your project:
- XButtonXP.cpp
- XButtonXP.h
- XThemeHelper.cpp
- XThemeHelper.h
- OddButton.cpp
- OddButton.h
- CreateGrayscaleIcon.h
Next, include header file XButtonXP.h in appropriate project files
(typically the dialog header file).
Now you are ready to start using CXButtonXP
.
The demo app shows how to call XButtonXP:
m_XButtonXP.SetIcon(IDI_MFC, CXButtonXP::LEFT);
You can combine XButtonXP function calls like this:
m_XButtonXP.SetIcon(IDI_MFC, CXButtonXP::LEFT)
.SetToggle(FALSE)
.EnableTheming(TRUE)
.SetDrawToolbar(FALSE);
Tips & Tricks
-
The button must not have
BS_OWNERDRAW
property
in dialog template.
-
When you include XButtonXP in your app, you might get error message
about the symbol
DFCS_HOT
, even though you have installed
Platform SDK.
To fix this, you need to put this line in
stdafx.h, before any includes:
#define WINVER 0x0500
-
The compiler error message fatal error C1083: Cannot open include file: 'uxtheme.h': No such file or directory
means that compiler cannot find uxtheme.h, which is in Microsoft
Platform SDK. This is free download from Microsoft site.
I would give url here, but Microsoft keeps changing download page
for Platform SDK, so best thing is to
google
for it.
-
When custom colors are used for text or
background, theming is automatically switched off.
Acknowledgments
Revision History
Version 1.3 - 2008 January 19
- Fixed bug when button is released and mouse is moved, reported by rootdial.
- Fixed problem with drawing as default button, reported by fredwobus and programbyran.
- Fixed resource leak in DrawIcon(), reported by grin.
- Fixed transparency bug for toolbar style, reported by Pandele Florin.
- Fixed bug where WM_COMMAND was being sent twice when return pressed, reported by fredwobus.
- Implemented WM_MOUSELEAVE handler, suggested by ksk, Roman Komary. fredwobus, 630596399 and EIEN.
- Added multi-line support, requested by jlatorref.
- Added grayscale support for disabled icon, requested by programbyran.
- Added support for custom text and background colors, requested by PatLeCat and Alex Evans.
- Added tooltips to demo.
- Added option to display message box to demo.
- Added option for hatch background to demo.
- Added option for different icon color formats to demo.
- Added option to select custom text and background colors to demo.
Version 1.2 - 2005 April 20
- Fixed bug with hot state, reported by ksk
- Added SetDrawToolbar() and GetDrawToolbar(), suggested by Dominik Reichl
Version 1.1 - 2005 March 30
- Fixed bug in drawing toggle button, reported by Dominik Reichl.
- Added SetToggleState()
- Removed
#include "XButtonXPTest.h"
from XButtonXP.cpp
Version 1.0 - 2005 March 22
Usage
This software is released under
The Code Project Open
License (CPOL). You are free to use this
software in any way you like, except that you may not sell this source code.
If you modify it or extend it, please to consider posting new code here for
everyone to share. This software is provided "as is" with no expressed or
implied warranty. I accept no liability for any damage or loss of business that
this software may cause.