Introduction
Recently my new client wanted a custom button for the new consumer application that I am building for him. Oh oh, I thought; buttons are difficult, because of the many states and modes you have to handle. Even worse, ownerdraw buttons require special treatment to look right under themed XP. The button my client wanted was similar to what is displayed when you select Search... from Explorer right-click menu:
 | The text of checkboxes is displayed in bold when they are selected. |
Mousing over one of these buttons produces another effect:
 | When the mouse cursor is moved over a checkbox, its text is underlined. |
The same two effects occur with radio buttons that are displayed on the Search panel.
Now my only question was, "How did Microsoft implement these?" Is it possible these buttons are not really ownerdraw? To investigate further, I turned to one of my favorite programming tools, HPS HwndSpy. To my relief, HPS HwndSpy showed that buttons were not ownerdraw:
Implementation Details
Now that I was sure I could implement this button without needing to make them ownerdraw, I decided that API could be very simple; the only option was display state: bold, underlined, or bold and underlined. So XEmphasisButton has two public APIs:
Emphasis GetEmphasis() { return m_eEmphasis; }
void SetEmphasis(Emphasis eEmphasis)
{
m_eEmphasis = eEmphasis;
SetEmphasisFont();
}
The choices for parameter are given by enum
:
enum Emphasis
{
NONE = 0,
BOLD, UNDERLINE, BOLD_AND_UNDERLINE
};
Since emphasis state is limited to four cases, programming is fairly simple. In PreSubclassWindow()
, I create four fonts, and in DefWindowProc()
I handle three messages:
BM_SETCHECK
— this message is sent when the check state of radio button or check box has changed:
if (message == BM_SETCHECK)
{
if (wParam == BST_CHECKED)
{
if (m_eEmphasis == BOLD || m_eEmphasis == BOLD_AND_UNDERLINE)
{
if (!m_bBold)
{
if (m_bUnderline)
SetFont(&m_fontBoldAndUnderline);
else
SetFont(&m_fontBold);
m_bBold = TRUE;
}
}
}
if (wParam == BST_UNCHECKED)
{
if (m_eEmphasis == BOLD || m_eEmphasis == BOLD_AND_UNDERLINE)
{
if (m_bBold)
{
if (m_bUnderline)
SetFont(&m_fontUnderline);
else
SetFont(&m_fontNormal);
m_bBold = FALSE;
}
}
}
}
WM_MOUSEMOVE
— this message is sent when cursor moves over button. I use it to detect when cursor has just entered button's client area, to enable underlining:
else if (message == WM_MOUSEMOVE)
{
if (!m_bOverControl) {
m_bOverControl = TRUE;
if (m_eEmphasis == UNDERLINE ||
m_eEmphasis == BOLD_AND_UNDERLINE)
{
if (!m_bUnderline)
{
if (m_bBold)
SetFont(&m_fontBoldAndUnderline);
else
SetFont(&m_fontUnderline);
m_bUnderline = TRUE;
}
SetTimer(1, 100, NULL); }
}
}
WM_TIMER
— timer is used to detect when mouse leaves client area of button:
else if (message == WM_TIMER)
{
if (m_bOverControl) {
CRect rect;
GetWindowRect(&rect);
CPoint point;
GetCursorPos(&point);
if (!rect.PtInRect(point))
{
if (m_bUnderline)
{
if (m_bBold)
SetFont(&m_fontBold);
else
SetFont(&m_fontNormal);
m_bUnderline = FALSE;
}
m_bOverControl = FALSE;
KillTimer(wParam);
}
}
}
Demo App
The demo app allows you to try out various combinations of display attributes:
How To Use
To integrate CXEmphasisButton
class into your app, you first need to add following files to your project:
- XEmphasisButton.cpp
- XEmphasisButton.h
Next, include header file XEmphasisButton.h in appropriate project files (usually, this will be in header file for dialog class). Then replace declaration of button control with this:
CXEmphasisButton m_MyButton;
(use whatever variable name already exists).
Now you are ready to start using CXEmphasisButton
. In dialog's OnInitDialog()
function, insert line
m_MyButton.SetEmphasis(CXEmphasisButton::BOLD_AND_UNDERLINE);
(use whatever display attributes you wish).
Revision History
Version 1.0 — 2006 August 15
Usage
This software is released into the public domain. You are free to use it 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.