Native Win32 Theme-Aware, Owner-Draw Controls (No MFC!)

Environment: Windows XP, VC++

Copyright © 2002 by Ewan Ward

1. Abstract

With the advent of Windows XP, Microsoft has attempted to ensure that a lot of the responsibility for rendering the user interface now lies with the operating system rather than the programmer. That's fine in most cases, but there are still some situations when you want to be able to draw the control yourself. One of the most common is a button with an image on it, and in this article we discuss implementing such a control using the native Win32 API. Although the discussion concerns owner-draw buttons specifically, the topics covered here will apply to all owner-draw controls.

In this article, we discuss how to implement a simple native Win32 application that contains a theme-aware, owner-draw button that will run on both themed and non-themed Microsoft Windows 32-bit operating systems.

2. Discussion

2.1. The Dialog Template

Open up the sample project (OwnerDraw.dsp) and look at IDD_MAIN_DLG. You will see that the UI comprises a dialog box with two buttons, one an owner-draw control button and one a normal push button (for reference). Open up the resource script (OwnerDraw.rc) as text and see that our owner-draw button is a normal system button but with the BS_OWNERDRAW button style flag set.

//////////////////////////////////////////////////////////////////
//
// Dialog
//

IDD_MAIN_DLG DIALOG DISCARDABLE  0, 0, 307, 122
STYLE DS_SETFOREGROUND | DS_CENTER | WS_POPUP | WS_VISIBLE |
                                                WS_CAPTION |
                                                WS_SYSMENU
CAPTION "Owner Draw Sample"
FONT 8, "MS Sans Serif"
BEGIN
    CONTROL         "Owner Draw",IDC_OWNERDRAW_BTN,"Button",
                    BS_OWNERDRAW |
                    WS_TABSTOP,80,20,105,35
    PUSHBUTTON      "Normal",IDC_NORMAL_BTN,80,60,105,35
    PUSHBUTTON      "OK",IDOK,250,5,50,14
    PUSHBUTTON      "Cancel",IDCANCEL,250,22,50,14
END

With this flag set, we are telling Windows that it is the responsibility of the button's owner (the dialog) to draw the button. You will find the implementation for the dialog in MainDlg.cpp.

2.2. What A State!

What a control looks like on the screen is a reflection of its state. A button, for example, could be pressed or focused. The pre-defined states that Windows can tell us about are...

State Description
ODS_CHECKED The menu item is to be checked. This bit is used only in a menu.
ODS_COMBOBOXEDIT The drawing takes place in the selection field (edit control) of an owner-drawn combo box.
ODS_DEFAULT The item is the default item.
ODS_DISABLED The item is to be drawn as disabled.
ODS_FOCUS The item has the keyboard focus.
ODS_GRAYED The item is to be grayed. This bit is used only in a menu.
ODS_HOTLIGHTWindows 98/Me, Windows 2000/XP: The item is being hot-tracked; that is, the item will be highlighted when the mouse is on the item.
ODS_INACTIVE Windows 98/Me, Windows 2000/XP: The item is inactive and the window associated with the menu is inactive.
ODS_NOACCEL Windows 2000/XP: The control is drawn without the keyboard accelerator cues.
ODS_NOFOCUSRECT Windows 2000/XP: The control is drawn without focus indicator cues.
ODS_SELECTED The menu item's status is selected.

...but obviously, not all of these apply to our button.

Although Windows will notify us if any of the relevant states listed above change, in our example we only redraw the button in the following situations:

  • It gains the focus
  • It loses the focus
  • It is pressed
  • It is released
  • (especially for XP) If it is "hot"

2.2.1. Hot Tracking

XP users may have noticed that system buttons are highlighted (or tracked) when the mouse is over the button. However, we don't receive any special notification from Windows when a system button is being hot-tracked. For that, we need to determine whether the mouse has moved over the button. In our example, we have used the old 16-bit technique of subclassing—basically, we have replaced the Windows procedure of the owner-draw button with our own procedure so that we can listen out for WM_MOUSEMOVE messages. When the mouse moves over the button, the procedure will receive the WM_MOUSEMOVE message and then we can redraw the button highlighted.

2.3. Getting The Message

It is the WM_DRAWITEM notification from the operating system that will prompt us to redraw the button. You can see that provided with the notification is information on the current state of the button according to Windows. Coupled with the extra state information gleaned from subclassing, we now have enough information at hand in order to render the button.

2.3.1. Double Clicks

When the owner-draw button is double-clicked, it sends out a BN_DBLCLK notification rather than responding as if it had been pressed and released twice over. To change this behaviour, we can make our owner-draw button post the WM_LBUTTONDOWN notification back to itself when it receives a WM_LBUTTONDBLCLK message.

2.4. Themes

So far, so good. The next problem is deciding what the button should look like. The first step is to determine whether the operating system supports themes. The function InitThemes() attempts to load UXTHEME.DLL dynamically. Don't try and statically link to the UXTHEME library; the application will fail to run on systems that don't ship with the DLL. We load the following functions:

  • OpenThemeData
  • GetThemeBackgroundContentRect
  • DrawThemeBackground
  • DrawThemeText
  • CloseThemeData

2.5. Drawing The Button

Drawing the button is then a four-stage process:

  • Stage 1—draw the background (including the frame).
  • Stage 2—draw the icon.
  • Stage 3—write on the text.
  • Stage 4—draw the focus rectangle.

3. Reusing The Sample Code

To add basic support of themes to your application, copy lines 27 to 82 (inclusive) from MainDlg.cpp. You must include the headers UXTHEME.H and TMSCHEMA.H. Remember also to include a manifest and to call InitCommonControls when your application is first launched.

The functions PrepareImageRect and DrawTheIcon are used to put the icon on the button. The WM_DRAWITEM handler demonstrates how to draw a themed and non-themed button, depending on the operating system.

4. Credits

The functions PrepareImageRect() and DrawTheIcon() are taken from the CButtonST class by Davide Calabró.

Downloads

Download source - 12 Kb
Download demo - 13 Kb


Comments

  • Reasons to everyone seems to be absolute wrong on shoes and as a consequence reasons why you must check out this review.

    Posted by BobHotgloff on 05/24/2013 02:39am

    Concepts of the shoes that you'll make money from beginning today. [url=http://www.shoesjp.biz/new-balance【ニューバランス】-c-670.html]ニューバランス[/url] Reasons almost anything you may have find out about shoes is truly entirely wrong and exactly what you need realize. [url=http://www.shoesjp.biz/nike【ナイキ】-c-634.html]ナイキスニーカー[/url] Brief post aids you with all inner workings linked with shoes together with what you want to perform straight away. [url=http://www.kutujp.biz/]アシックス[/url] New shoes Ebook Disclose Ways To Rule The shoes Marketplace [url=http://www.kutujp.biz/アディダス-adidas-c-4.html]アディダス シューズ[/url] Precisely why just about everything that you have discovered about sneakers is simply wrong and exactly what you need understand. [url=http://www.kutujp.biz/アシックス-asics-c-3.html]アシックス[/url] The most effective solution for shoes you could find out more about now. [url=http://www.kutujp.biz/ナイキ-nike-c-13.html]ナイキ スニーカー[/url] Unique website clearly shows the low down upon shoes and as well , why you have got to take action today. [url=http://www.kutujapan.org/]アディダス[/url] New shoes E book Shows The Best Ways To Dominate The shoes Marketplace [url=http://www.kutujapan.org/adidas-アディダス-c-74.html]アディダス シューズ[/url] All new shoes Publication Will show Easy Methods To Dominate The shoes World [url=http://www.kutujapan.org/new-balance-ニューバランス-c-13.html]ニューバランス キッズ[/url] Whatever the professionals are generally not thinking about shoes and how it is affecting you. [url=http://www.kutujapan.org/nike-ナイキ-c-78.html]ナイキ[/url] For what reason so many people are extremely wrong about shoes and as a consequence the reasons why you ought to check this study.

    Reply
  • Very useful

    Posted by mammuth on 07/24/2005 10:36am

    It helped me a lot :) Thanks!

    Reply
  • Small feature missing

    Posted by Legacy on 09/30/2003 12:00am

    Originally posted by: Steve Alpert

    This code doesn't handle the case of a disabled themed button. You must change

    the code at zDrawThemeText.

    Replace PBS_NORMAL in the call by:

    bIsDisabled ? PBS_DISABLED : PBS_NORMAL

    Reply
  • Thanks for the constructive comments, whj_sy ;-)

    Posted by Legacy on 01/18/2003 12:00am

    Originally posted by: Ewan Ward

    I don't mind a bit a bit of constructive criticism, but I am at a bit of a loss as to how to improve the article based on the previous comment!

    Regards


    Ewan.

    Reply
  • never seen so bad...

    Posted by Legacy on 01/13/2003 12:00am

    Originally posted by: whj_sy

    never seen so bad...

    Reply
  • Thanks, very timely sample

    Posted by Legacy on 12/19/2002 12:00am

    Originally posted by: Serguei Batchila

    Nice and very useful sample. I've been waiting for it for a long time.
    Thank you very much.

    However, I found copule of bugs which do not let me call you sample 'ideal':
    1. (not very important, because easy fixable) when you drag a mouse cursor over the button from left to right and back very fast, you can notice button's blinking during redraw. Meanwhile standart button doesn't blink. I guess you don't draw your button to a buffer (memory bitmap), do you?
    Lots of developers may say 'it's barely noticable', but these barely noticable things make a program look unprofessional.
    2. (more important and not so easy to fix) - Your way of hot-tracking isn't quite right. You handle WM_MOUSEMOVE in a dialog and reset 'bMouseOverButton' flag. If your dialog would have 20 buttons, would you have 20 'bMouseOverButton' flags? According to OOP theory, dialog must not be involved to solution of button's problems.
    Correct way is: when button receives WM_MOUSEMOVE, it sets 'Hot' flag it 'asks' the Windows to let it know when mouse will leave the button (by calling TrackMouseEvent()). Windows send a message WM_MOUSELEAVE, button handles the message and reset the 'Hot' flag.
    If you want more details, let me know, I will post a sample (or even send you a sample).

    Reply
  • tab to ownerdrawn button doubles default marking

    Posted by Legacy on 12/17/2002 12:00am

    Originally posted by: Sam Jost

    Just try tabbing through the buttons.
    Upon activating a button you'll see the marking for the default button move with tab, but when you activate the ownerdrawn button it will be marked as default button AND the OK-button will be marked, too.

    This seems to be a small optical bug...

    The rest of the code is quite helpful even to a non-mfc-programmer like me ;)

    Greetings,
    Sam

    Reply
  • CButtonST

    Posted by Legacy on 12/11/2002 12:00am

    Originally posted by: Davide Calabro'

    Well, it's nice to see that my CButtonST class helps someone to write new/improved controls, but a credit into the documentation/source code would be very welcome!
    Especially when the PrepareImageRect and DrawTheIcon functions are copied as they are from CButtonST.

    Davide Calabro'
    http://www.SoftechSoftware.it

    Reply
  • When WinXp use no theme, the button is dead

    Posted by Legacy on 12/09/2002 12:00am

    Originally posted by: ZHEFU ZHANG

    It is nice when there is a theme in WinXP, but, if you use classic Win32 layout, the button is invisible, there is only a green brush icon above the normal button, and I can not push it either.
    Maybe it will be better if you add handler to deal this situation in demo.
    Nice Work!

    Reply
  • Owner draw button double clicks

    Posted by Legacy on 12/09/2002 12:00am

    Originally posted by: John Wellbelove

    I've never tried it non-MFC, but I trap the double click messages and re-send them as single clicks. This solves the problem of unresponsive buttons.

    Reply
Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • On-demand Event Event Date: September 10, 2014 Modern mobile applications connect systems-of-engagement (mobile apps) with systems-of-record (traditional IT) to deliver new and innovative business value. But the lifecycle for development of mobile apps is also new and different. Emerging trends in mobile development call for faster delivery of incremental features, coupled with feedback from the users of the app "in the wild." This loop of continuous delivery and continuous feedback is how the best mobile …

  • Webinar on September 23, 2014, 2 p.m. ET / 11 a.m. PT Mobile commerce presents an array of opportunities for any business -- from connecting with your customers through mobile apps to enriching operations with mobile enterprise solutions. Join guest speaker, Michael Facemire, Forrester Research, Inc. Principal Analyst, as he discusses the new demands of mobile engagement and how application program interfaces (APIs) play a crucial role. Check out this upcoming webinar to learn about the new set of …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds