Click here to Skip to main content
15,867,308 members
Articles / Desktop Programming / Win32
Tip/Trick

Win32:C++ Add Icon to Right Side of Button Text

Rate me:
Please Sign up or sign in to vote.
4.23/5 (8 votes)
21 Nov 2021CDDL2 min read 9.8K   257   9   12
Easy to use class which supports displaying icons on button controls
In this tip, you will see an easy to use class to support displaying icons on button controls. Supports placing icon to the left or right of the text or center with text overlay.

Introduction

I needed to add a 16x16 graphic icon to a button with the icon to the right of the text, but I could only get BM_SETIMAGE to place it to the left of the text. After asking around online and researching how it may be done, I threw together this class to give me what I need. Thanks to everyone who provided helpful tips and information. I'm giving back so others don't have to hassle with the same thing.

I tested this back to NT4 SP4 to ensure apps can still run on those versions if needed.

Improvement Needed

The icons were put in an image list in an attempt to support drawing disabled icons. I was hoping to use DrawThemedIcon() for the proper look, but I couldn't get the function to do anything; so I abandoned it and just used the non-themed method for both themed and non-themed environments. It currently uses ILD_BLEND25 for disabled icons, however, it would be better to lighten the icon image to match what the default controls do because it looks better, and when using ILD_BLEND25 in a limited color environment, you can see the blue selection over the icon.

My .ico are only 16x16 so I hard coded the size, but in a way that could be improved easily, should someone need other sizes.

Using the Code

One method for use is to do the following:
  1. Include header file:
    C++
    #include "cownerdrawbutton.h"
    
  2. Setup icon map of button ID and Icon ID:
    C++
    //---------------------------------
    // Icon Map
    //---------------------------------
    static COwnerDrawButton::sIconMap OwnerDrawIconMap[]={
      { IDI_ICONOK, IDOK, COwnerDrawButton::sIconMap::RIGHT },
      { IDI_ICONCANCEL, IDCANCEL, COwnerDrawButton::sIconMap::LEFT },
      { IDI_ICONHELP, IDHELP, COwnerDrawButton::sIconMap::LEFT },
    };
    
  3. Setup a global variable and reference the map:
    C++
    COwnerDrawButton OwnerDrawButton(OwnerDrawIconMap, _countof(OwnerDrawIconMap));
    
  4. Setup the dialog callback procedure with:
    C++
    case WM_INITDIALOG:
    {
      OwnerDrawButton.SetupButtonsForHWND(hwnddlg, g_hResInstance);
      break;
    }
    
    case WM_DRAWITEM:
    {
      LRESULT lr=OwnerDrawButton.DrawItem(reinterpret_cast<LPDRAWITEMSTRUCT>(lparam));
      if (lr!=-1) {
        return lr;
      }
      break;
    }
    
    case WM_NOTIFY:
    {
      // check if notify message for our custom draw buttons
      LRESULT lr=OwnerDrawButton.CustomDrawItem(reinterpret_cast<LPNMCUSTOMDRAW>(lparam));
      if (lr!=-1) {
        SetWindowLongPtr(hwnd, DWLP_MSGRESULT, lr); //use this in dialog procedure
        return lr;
      }
      break;
    }
    
  5. That's all folks!

MFC

It wasn't designed for MFC but can still be used with. Here's one example:
  1. Include header file:
    C++
    #include "cownerdrawbutton.h"
    
  2. Setup icon map of button ID and Icon ID:
    C++
    //---------------------------------
    // Icon Map
    //---------------------------------
    static COwnerDrawButton::sIconMap OwnerDrawIconMap[]={
      { IDI_ICONOK, IDOK, COwnerDrawButton::sIconMap::RIGHT },
      { IDI_ICONCANCEL, IDCANCEL, COwnerDrawButton::sIconMap::LEFT },
      { IDI_ICONHELP, IDHELP, COwnerDrawButton::sIconMap::LEFT },
    };
    
  3. Setup a global variable and reference the map:
    C++
    COwnerDrawButton OwnerDrawButton(OwnerDrawIconMap, _countof(OwnerDrawIconMap));
    
  4. Setup the dialog with:
    //-------------------
    // CMyDlg dialog
    //-------------------
    
    class CMyDlg : public CDialogEx
    {
      // ...
    
      afx_msg void OnDrawItem(int nIDCtl, LPDRAWITEMSTRUCT lpDrawItemStruct);
      virtual BOOL OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult);
    };
    
    //-------------------
    // WM_INITDIALOG
    //-------------------
    
    CMyDlg::OnInitDialog()
    {
      CDialogEx::OnInitDialog();
    
      OwnerDrawButton.SetupButtonsForHWND(GetSafeHwnd(), AfxGetInstanceHandle());
    
      // ...
    
      return TRUE;
    }
    
    //-------------------
    // MFC Message Map
    //-------------------
    
    BEGIN_MESSAGE_MAP(CMFCButtonIconsDlg, CDialogEx)
      // ...
      ON_WM_DRAWITEM()
    END_MESSAGE_MAP()
    
    //-------------------
    // WM_DRAWITEM
    //-------------------
    void CMyDlg::OnDrawItem(int nIDCtl, LPDRAWITEMSTRUCT lpDrawItemStruct)
    {
      LRESULT lr=OwnerDrawButton.DrawItem(lpDrawItemStruct);
      if (lr!=-1) {
        return;
      }
      CDialogEx::OnDrawItem(nIDCtl, lpDrawItemStruct);
    }
    
    //-------------------
    // WM_NOTIFY
    //-------------------
    BOOL CMyDlg::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult)
    {
      // check if notify message for our custom draw buttons
      LRESULT lr=OwnerDrawButton.CustomDrawItem(reinterpret_cast<LPNMCUSTOMDRAW>(lParam));
      if (lr!=-1) {
        *pResult=lr;
        return TRUE;
      }
    
      return CDialogEx::OnNotify(wParam, lParam, pResult);
    }
    
  5. That's all folks!

History

  • 11th November, 2021: Release 1.0
  • 13th November 2021: Release 1.01
    • Builds in non-unicode (multi-byte) configurations. 
    • made dll handle / function pointer static to use less memory if using multiple variables.

License

This article, along with any associated source code and files, is licensed under The Common Development and Distribution License (CDDL)


Written By
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
SuggestionSome details to add Pin
merano9926-Nov-21 12:51
mvemerano9926-Nov-21 12:51 
GeneralMessage Closed Pin
21-Nov-21 22:24
pathan khan21-Nov-21 22:24 
GeneralMy vote of 5 Pin
Ștefan-Mihai MOGA21-Nov-21 22:11
professionalȘtefan-Mihai MOGA21-Nov-21 22:11 
QuestionDoes it also work with MFC Pin
merano9920-Nov-21 13:12
mvemerano9920-Nov-21 13:12 
Tried to get it running with MFC. Apparently the project is only suitable for Win32 API programs at first. Found several places that are incompatible. It would just work with MFC if the class could be derived from CButton, the constructor would not need any parameters and DrawItem () as declared below were. A full demo would help. It would take quite a bit of time and invest in know-how.

C++
class CMyBtn : public CButton
{
// Construction
public:
	CMyBtn();

// Overrides
	// ClassWizard generated virtual function overrides
	//{{AFX_VIRTUAL(CMyBtn)
	public:
	virtual void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct);
...
}

It would be too nice if the existing buttons could simply be exchanged for this class.
AnswerRe: Does it also work with MFC Pin
REALTBU21-Nov-21 15:19
REALTBU21-Nov-21 15:19 
GeneralRe: Does it also work with MFC Pin
merano9923-Nov-21 12:11
mvemerano9923-Nov-21 12:11 
AnswerRe: Does it also work with MFC Pin
djailless22-Nov-21 23:49
djailless22-Nov-21 23:49 
GeneralRe: Does it also work with MFC Pin
merano9923-Nov-21 12:21
mvemerano9923-Nov-21 12:21 
GeneralRe: Does it also work with MFC Pin
Member 568844323-Nov-21 23:49
Member 568844323-Nov-21 23:49 
GeneralRe: Does it also work with MFC Pin
merano9924-Nov-21 5:26
mvemerano9924-Nov-21 5:26 
Questioncould be better Pin
Southmountain16-Nov-21 8:06
Southmountain16-Nov-21 8:06 
AnswerRe: could be better Pin
REALTBU21-Nov-21 15:21
REALTBU21-Nov-21 15:21 
GeneralRe: could be better Pin
PBorchert24-Nov-21 6:53
PBorchert24-Nov-21 6:53 

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.