Introduction
I was in the
middle of updating a program that I’d developed
under NT and Win2K and I’d used
CBitmapButton
throughout the program. When porting over a new
version of the program to run under XP, I noticed
that my "old" bitmap buttons in
VC++ 7 weren’t
style aware, and made the app look a tad dated. A
search on the web brought my attention to a couple
of other examples of hover buttons, but none which
took my investment with
CBitmapButton
into consideration. I wanted a
CBitmapButton
derived class that I could just plug into my older
app. This is the first article I’ve submitted
here, so excuse my prose.
What’s my Theme?
Using
UXTHEME.DLL,
as I found in an example
project by Ewan Ward. I created a new
CTheme
class. If you prefer, you can use the WTL
CTheme
class. However, I just created one that’s
suitable for my purpose.
If you’re
creating an MFC Doc/View application, then in
CMainFrame
declare a CTheme
member m_theme
,
if it’s a dialog based app – in the
CDialog
derived main window declare a
CTheme
member m_theme
:
In a Doc/View
app, in
CMainFrame::OnCreate()
,
or in a Dialog based app in
OnInitDialog()
add:
The function
CTheme::Init()
calls a function
GetAppearance()
to see if the style is XP Style or Windows Classic
Style. Actually, it just checks if the current OS
is XP, and if the Classic Style is selected.
Collapse
BOOL CTheme::GetAppearance(void)
{
OSVERSIONINFO osvi;
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
GetVersionEx(&osvi);
if (osvi.dwMajorVersion < 5) return FALSE;
HKEY hKey;
CString szSubKey = _T("Control Panel\\Appearance");
CString szCurrent = _T("Current");
DWORD dwSize = 200;
unsigned char * pBuffer = new unsigned char[dwSize];
memset(pBuffer, 0, dwSize);
if (RegOpenKeyEx(HKEY_CURRENT_USER, (LPCSTR)szSubKey, 0L, KEY_READ, &hKey)
!= ERROR_SUCCESS)
{
delete []pBuffer;
return FALSE;
}
RegQueryValueEx(hKey, szCurrent, NULL, NULL, pBuffer, &dwSize);
RegCloseKey(hKey);
szCurrent = pBuffer;
delete []pBuffer;
if (szCurrent == _T("Windows Standard"))
return FALSE;
return TRUE;
}
If
CTheme::Init()
is never called, then
CHoverBitmapButton
will default to using the older button style. If
the style is XP Style, then the DLL is loaded.
What about the
bitmaps?
You should start
with the same bitmaps as you used in the stock
CBitmapButton
class, which includes drawing the edge. For the
XP style buttons, create (or copy) your old button
bitmaps with a white background – you don’t have
to draw the edges as you did with the old
CBitmapButton
. For example, I had a button:
(directU.bmp),
and for the XP style, I created a new bitmap:
(xpdirectU.bmp).
For the older style I had F, X and D bitmaps as
well; for the XP style, I only needed the U and X
bitmaps. I also named the resources the same –
except I prefixed the new XP style resource names
with the letters XP. This is important for the
CHoverBitmapButton
class to differentiate between the XP and Classic
style bitmaps. You may set the prefix yourself
using the public function
CHoverBitmapButton::SetPrefix()
– or just use the default.
The bitmaps are
loaded the same as before with either
CHoverBitmapButton::Load()
or
CHoverBitmapButton::AutoLoad()
.
In the example
Dialog application – in
OnInitDialog()
:
m_CtrlButton.Load(IDC_DIRECT, this, &m_theme);
In a Doc/View
application you could use the normal:
m_CtrlButton.AutoLoad(IDC_DIRECT, this);
and in the
CHoverBitmapButton
constructor,
m_pTheme
can be
initialized using a pointer to the variable
m_theme
via
CHoverBitmapButton::GetFrame()
.
So for the 2
styles – the bitmap resources would be named:
State |
Classic Windows Style |
|
XP Style |
|
UP |
"DIRECTU" |
|
"XPDIRECTU" |
|
DOWN |
"DIRECTD" |
|
|
|
FOCUSED |
"DIRECTF" |
|
|
|
DISABLED |
"DIRECTX" |
|
"XPDIRECTX" |
|
The new
XP Style
buttons will be drawn using the DLL function
DrawThemeBackground
that draws the button texture and edges.
Change my style
To handle the
message
WM_THEMECHANGED
,
you have to set
WINVER, _WIN32_WINNT, _WIN32_WINDOWS
and _WIN32_IE
all equal to
0x0501
in stdafx.h
If you don’t want
to detect the changes, then you could have an app
that runs under previous versions of Windows, but
if your only target is XP, then the example
includes the code to handle the
WM_THEMECHANGED
message.
Includes
Add the line
to stdafx.h
Using the class
As has been
mentioned, the use is slightly different between a
Dialog type app vs. a Doc/View type app.
Dialog type app:
In the main
CDialog
derived class’s
header file:
#include "theme.h"
#include "HoverBitmapButton.h"
and add a public
class member:
Change the
CButton
controls that you added with ClassWizard to:
CHoverBitmapButton m_CtrlButton;
And in
OnInitDialog
:
m_theme.Init(m_hWnd);
m_CtrlButton.Load(IDC_DIRECT, this, &m_theme);
Doc/View type
app:
In
MainFrame.h:
and add a public
class member:
And in
CMainFrame::OnCreate():
In a
CDialog
derived class which
has a button, in the header file:
#include "HoverBitmapButton.h"
Change the
CButton
controls that you added with ClassWizard to:
CHoverBitmapButton m_CtrlButton;
And in
OnInitDialog
:
m_ CtrlButton.AutoLoad(IDC_DIRECTORY, this);
Code changes
required:
For Doc/View,
change the code in HoverBitmapButton.cpp
where the comments show Doc/View.
In the creator:
CHoverBitmapButton::CHoverBitmapButton()
{
m_bHovering = FALSE;
m_pTheme = &(GetFrame()->m_theme); m_bXPTheme = m_pTheme->m_bXPTheme;
m_szXPPrefix = _T("XP");
m_bThemeChanging = FALSE;
}
In the Header and
the code file, uncomment the function
CMainFrame* CHoverBitmapButton::GetFrame(void)
Credits
Thanks to Ewan Ward
for the work in his Native Win32 Theme aware
Owner-draw Controls without
MFC.