Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / desktop / Win32

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

4.23/5 (8 votes)
21 Nov 2021CDDL2 min read 11.8K   262  
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)