Click here to Skip to main content
Email Password   helpLost your password?

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:

screenshot The text of checkboxes is displayed in bold when they are selected.

Mousing over one of these buttons produces another effect:

screenshot 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:

screenshot

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,         // button text will be bold if GetCheck() is TRUE
        UNDERLINE,    // button text will be underlined if mouse over
        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:

Demo App

The demo app allows you to try out various combinations of display attributes:

screenshot

How To Use

To integrate CXEmphasisButton class into your app, you first need to add following files to your project:

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.

You must Sign In to use this message board.
 
 
Per page   
 FirstPrevNext
GeneralMy 5 and thanks
Franc Morales
22:20 26 Jun '08  
A great little contribution, as always. Useful and concise. Pity we cannot modify the color on hover without owner-drawn, that would be just perfect.

I decided to implement WM_MOUSELEAVE with TrackMouseEvent rather than WM_TIMER. It seems to me that it is a more efficient solution. Perhaps it is just a matter of preference...

Anyway, your articles set the standard. Simple as that. Thank you very much.
GeneralRe: My 5 and thanks
Hans Dietrich
22:39 26 Jun '08  
Thanks for kind words. Let me know how WM_MOUSELEAVE works for you. At one point, I tried to use it in this control, and found that it missed some mouse leaves. Maybe due to some mistake I make? I'm not sure.


GeneralRe: My 5 and thanks
Franc Morales
1:08 27 Jun '08  
This is my code... I've changed some things but it should be no problem to see what I did.


else if( message == WM_MOUSEMOVE )
{
if( !m_bHot )
{
m_bHot = TRUE;

if( m_bChecked )
{
SetFont( &m_fBoldAndUnderline );
}
else
{
SetFont( &m_fUnderline );
}

TRACKMOUSEEVENT csTME;

csTME.cbSize = sizeof( csTME );
csTME.dwFlags = TME_LEAVE;
csTME.hwndTrack = this->m_hWnd;

::_TrackMouseEvent( &csTME );
}
}
else if( message == WM_MOUSELEAVE )
{
if( m_bHot )
{
m_bHot = FALSE;

if( m_bChecked )
{
SetFont( &m_fBold );
}
else
{
SetFont( &m_fNormal );
}
}
}


The logic is quite straight forward. If it fails... well, I'm in trouble. I do this kind of thing all the time.
GeneralNice article
Tamal Saha
11:21 23 Oct '07  
Hi,
U r a genius. That shows in all your articles.Smile
GeneralWM_MOUSELEAVE?
Perry Zhu
22:22 4 Jun '07  
I remember there is a message indicating the mouse cursor left the window.
Refer to http://msdn2.microsoft.com/en-us/library/ms645615.aspx[^]
But TrackMouseEvent() should be called to enable this notificationSmile

旧日重来

GeneralRe: WM_MOUSELEAVE?
Hans Dietrich
0:09 5 Jun '07  
What does this have to do with XEmphasisButton? Do you think WM_MOUSELEAVE should be used? If so, could you explain why?


GeneralRe: WM_MOUSELEAVE?
Perry Zhu
23:35 5 Jun '07  
Sorry, my fault.
You setup a timer to determine when the mouse leaves, and I think WM_MOUSELEAVE is more effective and preciseSmile

旧日重来

GeneralCompliment
virtualnik
9:59 30 May '07  
Hans,

Your work is really very much appreciated. All of your articles consist of a very high quality and i thank you very much for sharing your experience with us here at codeproject.

Thanks,
Nikolaus
GeneralI've been meaning to thank you...
Tom Guidarelli
6:56 29 May '07  
I just want to echo the others who have complemented you on your excellent work. Thank you very much for posting so many useful articles!

Tom
GeneralRe: I've been meaning to thank you...
Hans Dietrich
17:40 29 May '07  
Thank you for kind words.


GeneralNice control!
J Whattam
17:03 23 Aug '06  
First up a simple and effective control, you've got my five votes. But I was wondering why you didn't make the enumeration a set of binary flags so that bold underline becomes a combination of two flags which makes your code for checking bold state simpler.

Regards,
John.

GeneralSimple, but very useful
Anna-Jayne Metcalfe
20:41 21 Aug '06  
A very simple, but useful class. Thanks! Rose

Anna Rose

Currently working mostly on: Visual Lint Cool

Anna's Place | Tears and Laughter

"Be yourself - not what others think you should be"
- Marcia Graesch

"Anna's just a sexy-looking lesbian tart"
- A friend, trying to wind me up. It didn't work.

GeneralVery nice!...and a question about menu items in bold
Alexandru Matei
0:34 21 Aug '06  
Is it possible to make text menu items displayed in bold, but without making them owner drawn?

Thank you very much
GeneralRe: Very nice!...and a question about menu items in bold
Hans Dietrich
17:49 21 Aug '06  
I suspect the answer is no, but you should ask in the MFC forum, where someone who has written custom menu might see your question.
GeneralRe: Very nice!...and a question about menu items in bold
Anna-Jayne Metcalfe
20:39 21 Aug '06  
SetMenuDefaultItem() will do this for you. It's built into the Win32 SDK, so no owner drawing should be required - provided you only need to do this with one item at a time.

Anna Rose

Currently working mostly on: Visual Lint Cool

Anna's Place | Tears and Laughter

"Be yourself - not what others think you should be"
- Marcia Graesch

"Anna's just a sexy-looking lesbian tart"
- A friend, trying to wind me up. It didn't work.

GeneralExcellent as always
John R. Shaw
7:25 20 Aug '06  
Thanks for contenuing to share your code (which is excellent). The following is unimportant and is mainly for your many readers.

I am going back to school, after many years, and saw a minor detail in your code that has always bothered me in the past. I only mention it because I have seen this in sample code presented by an instructor and consider it a fundamental flaw. It has no real affect on the code, other than executing an extra instruction.

if( CHECKED ) { do something }
if( UNCHECKED ) { do something }


Not using 'if else' is just a minor detail that bothers me personaly. The sample code I refered to above did half a dozen additional tests instead of just the one, like your code.

Thanks,

INTP
"Program testing can be used to show the presence of bugs, but never to show their absence."Edsger Dijkstra

GeneralRe: Excellent as always
Hans Dietrich
7:37 20 Aug '06  
I am very probably guilty of what you say, but after a quick check of the XEmphasisButton.cpp file, I could find no such occurrences. Or were you referring to something else? Confused

Thanks,
Hans

AnswerRe: Excellent as always
KarstenK
4:17 24 Aug '06  
He wanted to optimize that:

if(wParam == BST_CHECKED)
{
}
else if(wParam == BST_UNCHECKED) //he misses the else
{
}

Greetings from Germany

GeneralRe: Excellent as always
Hans Dietrich
20:29 20 Aug '06  
Never mind. I just realized that the code snippet in the article is a preliminary version of the code in the .cpp file - which doesn't do the nasty thing you referred to. Smile

Thanks for keeping me honest,
Hans
GeneralRe: Excellent as always
John R. Shaw
7:41 22 Aug '06  
Red facedWhoops! Sorry about not pointing to the snippet. It is nice to have someone like you around. I tend to upset some people when I write about such things.

A software engineering book I am reading, says the statement "programming is an art form" is an old Myth. I say look at Hans code; if you do not recognize art when you see it, then you are not a programmer.Smile

INTP
"Program testing can be used to show the presence of bugs, but never to show their absence."Edsger Dijkstra

GeneralExcellent!
Wes Aday
7:07 18 Aug '06  
Another excellent article.
GeneralGreat job !!!
Leopoldo Peralta
14:36 17 Aug '06  
Thanks again for sharing your superb code with us.

Leopoldo Peralta
GeneralOther features
alexey N
21:26 16 Aug '06  
What about text behavior ? (try to resize explorer "find panel")
I think that Microsoft use some kind of Html rules.

My english is not so good. Please, correct my errors.
Best regards, Alexey.

GeneralRe: Other features
Hans Dietrich
4:12 18 Aug '06  
Yes, I noticed that, too. It's obvious that the shrinking/expanding of the buttons is being controlled by the search panel container, which is a simple dialog, as you can see from this screenshot of HPS HwndSpy.

I say "obvious" because I don't believe it's realistic to have the control resize and reposition itself, since there are other controls above and below it. One feature that might be useful along those lines is if the control would have an API that returned the vertical height necessary to display all of its text, given a horizontal width. Hmmm....


Last Updated 23 May 2007 | Advertise | Privacy | Terms of Use | Copyright © CodeProject, 1999-2010