Click here to Skip to main content
15,878,852 members
Articles / Desktop Programming / MFC
Article

XIcon - an MFC control to display text and icon

Rate me:
Please Sign up or sign in to vote.
4.90/5 (21 votes)
4 May 2008CPOL6 min read 52.2K   1.3K   40   4
XIcon is an MFC control that displays text and/or icon with auto-sizing.

Introduction

One of my clients recently showed me a competitor's product that had some very nice user interface features. One of the features was a groupbox that displayed a dynamic header and icon - the icon changed to indicate current status of the information displayed inside the groupbox. I thought this would be trivial to copy, so naturally I said I could do it.

Of course I was thinking of the trick I used with displaying a checkbox in the groupbox header, like I did in my EnableGroupboxControls article. By placing a checkbox control over the groupbox header, but after the groupbox control in tab order, it is possible to make the checkbox appear to be integrated with the groupbox. My plan was to do the same thing to display an icon and text over the groupbox header, using a simple CStatic-based control.

The result looked very good. I cannot show you how it looked, because I scrapped this idea in about five seconds. Unlike user-interactive controls (like buttons), CStatic controls are assumed not to change, and this meant that paint messages were not being sent to CStatic control in a consistent manner, causing icon and text to suddenly disappear.

Since this is the way non-interactive controls behaved, my next choice was to base the new icon control on CButton. Making the button ownerdraw and putting all the display code in the DrawItem() function, I quickly came up with CXIcon, which proved to be stable and without the painting problems of earlier CStatic control.

Although XIcon is based on CButton, XIcon objects do not act like buttons, and they do not display the usual hot or pressed state.

XIcon Features and Behaviors

The XIcon demo app shows how XIcon control may be used with groupbox:

screenshot

This screenshot shows some of the text effects and icon alignments. XIcon buttons 1 and 2 use BS_LEFT, and XIcon buttons 3 and 4 use BS_RIGHT.

The bar that is placed inside each groupbox shows the width of the XIcon button as it is in the dialog template. In this screenshot, automatic resizing is turned on, and so the XIcon button resizes itself to the width of the text and icon.

Compare this with the way it looks when automatic resizing is turned off:

screenshot

In this screenshot the XIcon buttons do not resize themselves. The areas outlined in red show the over-sizing or under-sizing, which correspond to the size of the bar placed inside the groupbox.
With XIcon I now had a control that I could use with groupbox or anyplace I needed icon + text.

XIcon API

FunctionDescription
BOOL GetAutoResize()Retrieves auto resize setting
BOOL GetBold()Retrieves bold setting
CFont* GetFont()Retrieves pointer to CFont member variable
BOOL GetFont(LOGFONT *pLF)Retrieves LOGFONT struct for font
CString GetFontFaceName()Retrieves font face name
int GetFontPointSize()Retrieves font point size
int GetIconSpacing()Retrieves spacing between icon and text
void GetMargins(int& nXMargin, int& nYMargin)Retrieves x and y margins
COLORREF GetTextColor()Retrieves text color
void Resize()Resize control based on current text, font, and icon setting
CXIcon& SetAutoResize(BOOL bAutoResize, BOOL bRedraw = TRUE)Sets auto resizing option
CXIcon& SetBold(BOOL bBold, BOOL bRedraw = TRUE)Sets bold font
CXIcon& SetFont(CFont *pFont, BOOL bRedraw = TRUE)Sets font via CFont object
CXIcon& SetFont(LOGFONT * pLogFont, BOOL bRedraw = TRUE)Sets font via LOGFONT struct
CXIcon& SetFont(LPCTSTR lpszFaceName, int nPointSize, BOOL bRedraw = TRUE)Sets font face name and point size
CXIcon& SetIcon(HICON hIcon, UINT nIconSize = 16, BOOL bRedraw = TRUE)Sets icon via HICON
CXIcon& SetIcon(UINT nIconId, UINT nIconSize = 16, BOOL bRedraw = TRUE)Sets icon via resource id
CXIcon& SetIconSpacing(int nIconSpacing, BOOL bRedraw = TRUE)Sets spacing between icon and text
CXIcon& SetMargins(int nXMargin, int nYMargin, BOOL bRedraw = TRUE)Sets x and y margins
CXIcon& SetTextColor(COLORREF cr, BOOL bRedraw = TRUE)Sets text color
CXIcon& SetWindowText(LPCTSTR lpszText, BOOL bRedraw = TRUE)Sets text

Implementation Details

I have already mentioned that XIcon is based on ownerdraw CButton control, and uses DrawItem() function to implement drawing. To reduce flicker to minimum, I also used DC double-buffering when drawing text and icon.

In demo app, I have used file globe.ico that I extracted from image library that is included with Visual Studio®. This library (VS2005ImageLibrary.zip) is located in Common7 directory.

The only other thing you should be aware of is that you can supply icon to XIcon either by passing HICON handle, or by passing resource id of icon. If passing icon, it is up to caller to make sure icon is destroyed; if passing resource id, XIcon will call DestroyIcon() itself.

Note that there is no linkage between XIcon buttons and groupboxes. Here is dialog template for demo app, with groupbox/XIcon pairs highlighted:

screenshot

Overlaying the groupbox header with XIcon will work only if you know the trick: the groupbox must precede XIcon in tab order. Tab order is simply the order in which controls appear in the dialog template. If the groupbox came after XIcon, it would overlay XIcon, and the XIcon would not be visible to the user. This works this way because the order of controls in the dialog template is also the order in which the controls are created and displayed at run time.
You can set the tab order inside Visual Studio®, but with overlapping controls it is not completely straightforward. Here is what the dialog template looks like with tab order labels made visible by menu command Format | Tab Order:

screenshot

The groupbox headers that are outlined in red are the ones that have been overlaid by XIcon controls. Since the tab order labels for groupboxes and XIcon controls are displayed over each other, it is difficult to set the tab order in VS IDE. However, as usual, there is a trick: temporarily move the XIcon control above or below the groupbox, and then edit the tab order. When you are finished, move XIcon control back.

How to use

Step 1 - Add Files

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

  • XIcon.cpp
  • XIcon.h

Step 2 - Add Header File to Your Source Module

In the module where you want to use XIcon (typically this will be dialog header file), include header file XIcon.h .

Step 3 - Add Button Control to Dialog

Using the VS IDE resource editor, add a button control where you want the XIcon control (make sure to select BS_OWNERDRAW style), and associate button control with a class variable. Then replace CButton with CXIcon:

screenshot

Step 4 - Add Initialization Code

Add initialization code to OnInitDialog() function:
m_IconButton1.SetBold(TRUE, FALSE);
// because no other attributes are being set,
// it is necessary to tell control to resize itself
m_IconButton2.Resize();
m_IconButton3.SetFont(_T("Comic Sans MS"), 10, FALSE);
m_IconButton4.SetWindowText(MAKEINTRESOURCE(IDS_BUTTON_4), FALSE)
             .SetTextColor(RGB(0,0,255), FALSE);
LoadIcons();
LoadIcons() is function to load selected icon:
void CXIconTestDlg::LoadIcons()
{
    UpdateData(TRUE);

    if (m_hGlobeIcon)
        ::DestroyIcon(m_hGlobeIcon);
    m_hGlobeIcon = 0;

    static UINT nIconId[] = { IDI_T256, IDI_T32BPP };

    if (m_nStyle == 0 || m_nStyle == 2)
    {

        // Note: you can also use LoadIcon(), if you are sure that 
        // you will get correct icon
        m_hGlobeIcon = (HICON) ::LoadImage(AfxGetInstanceHandle(), 
                                   MAKEINTRESOURCE(nIconId[m_nIconType]),
                                   IMAGE_ICON, 16, 16, 0);
        ASSERT(m_hGlobeIcon);
    }

    m_IconButton1.SetIcon(m_hGlobeIcon);
    m_IconButton2.SetIcon(m_hGlobeIcon);
    m_IconButton3.SetIcon(m_hGlobeIcon);
    // pass icon resource id
    m_IconButton4.SetIcon(m_nStyle == 1 ? 0 : nIconId[m_nIconType]); 
}

Revision History

Version 1.0 - 2008 May 4

  • Initial public release

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.


License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior) Hans Dietrich Software
United States United States
I attended St. Michael's College of the University of Toronto, with the intention of becoming a priest. A friend in the University's Computer Science Department got me interested in programming, and I have been hooked ever since.

Recently, I have moved to Los Angeles where I am doing consulting and development work.

For consulting and custom software development, please see www.hdsoft.org.






Comments and Discussions

 
GeneralIcon background color is off Pin
Corkky28-Oct-09 16:29
Corkky28-Oct-09 16:29 
GeneralRe: Icon background color is off Pin
Hans Dietrich28-Oct-09 16:47
mentorHans Dietrich28-Oct-09 16:47 
GeneralRe: Icon background color is off Pin
Corkky29-Oct-09 7:11
Corkky29-Oct-09 7:11 
Thanks, Hans for replying so quickly Smile | :)

I don't have the icons physically in my resource file. I'm trying to display Windows system icons dynamically and re-size them down to 16x16 (no matter what their original size was). The icons may be different, depending on different criteria.

I'm just loading the icon dynamically using LoadIcon(), with the ID of a system icon I need at the moment like this...

hIcon = LoadIcon(NULL, IDI_INFORMATION); // IDI_INFORMATION, IDI_WARNING, IDI_HAND, etc.
// (typically used by Windows in its message boxes )

and then using the hIcon handle to pass it to your SetIcon function like this:

m_BT_icon.SetIcon(hIcon);

Here's a screenshot: http://img408.imageshack.us/img408/8827/screenshotn.jpg

The icon's background color is a bit darker than the client area's background. It's strange, because your DrawItem() code where you get the background color looks ok, it should work just fine. ???

Thanks for your help! Smile | :)

-Mari
GeneralGreat work as usuall.. Pin
enhzflep5-May-08 1:56
enhzflep5-May-08 1:56 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.