MFC Button Coloring
Summer 2002
Table of Contents
1. Introduction
2. Source Code
Instructor Comments
Section 1. Introduction

This example demonstrates how to create a button whose color may be changed at runtime. It relies on a class written by Bob Ryan which was subsequently modified by R. Wuppinger.

A simple project with a working button is available here.

Figure 1: Example application

The basic steps are to

  1. Create an MFC application,
  2. Add one or more button(s) using the class wizard,

    Figure 2: Added Button1

  3. Add the CColorButton class files (colorBtn.h and colorBtn.cpp) to the project,
  4. Create a CColorButton object for each button you wish to color,
  5. Attach each button control to the corresponding color button object,
  6. Make sure the button has the Owner Draw style selected (rigth click on button, Properties -> Styles -> Owner Draw), and

    Figure 3: "Owner Draw" selected

  7. Use CColorButton::SetColor to set the color of the button.

I have included snips of the code from the buttonColoring project along with the source code for the CColorButton class.

Section 2. Source Code
buttonColoringDlg.h
   1// buttonColoringDlg.h : header file
   2//
   3
   4#include "colorBtn.h" // CCT

We need to add this include because we will be declaring CColorButton objects in this file.

  . 
  .  SNIP
  . 
  35protected:
  36   HICON m_hIcon;
  37
  38   CColorButton m_button1; // CCT

Here I added a CColorButton object to correspond to the IDC_BUTTON1 control (label "Button1" in the figure). One CColorButton is required for each button that needs to be colored.

  . 
  .  SNIP
  . 
  54#endif // !defined(AFX_BUTTONCOLORINGDLG_H__359741D6_4334_4185_A496_19A2442ADB05__INCLUDED_)
buttonColoringDlg.cpp
   1// buttonColoringDlg.cpp : implementation file
   2//
   3// All modifications to the default MFC project are denoted with a "// CCT"
   4//  at the end of the line               // CCT
   5// Important: Push Button Properties -> Styles -> Owner Draw must be checked // CCT
   6#include "stdafx.h"
   7#include "buttonColoring.h"
   8#include "buttonColoringDlg.h"
   9#include "colorBtn.h" // CCT
  . 
  .  SNIP
  . 
  94
  95BOOL CButtonColoringDlg::OnInitDialog()
  96{
  . 
  .  SNIP
  . 
 122   // TODO: Add extra initialization here
 123   m_button1.Attach(IDC_BUTTON1, this); // CCT

We need to connect the CColorButton object to the button control. We do this using the CColorButton::Attach function. The first argument is the control ID, the second argument is the current window. Additional optional arguments can be used to set the initial color of the button.

 124   
 125   return TRUE;  // return TRUE  unless you set the focus to a control
 126}
  . 
  .  SNIP
  . 
 177// t a y l o r@msoe.edu, 6-13-2002 // CCT  178void CButtonColoringDlg::OnButton1()

This function is called when the button is pressed. In this example, the button cycles through four different background colors. The colors have been defined in the colorBtn.h file.

 179{
 180   // TODO: Add your control notification handler code here
 181   static int currentColor = 0;
 182   switch (currentColor) {
 183   case 0:
 184      m_button1.SetColor(BLACK, BLUE);
 185      currentColor = 1;
 186      break;
 187   case 1:
 188      m_button1.SetColor(BLACK, RED);
 189      currentColor = 2;
 190      break;
 191   case 2:
 192      m_button1.SetColor(BLACK, YELLOW);
 193      currentColor = 3;
 194      break;
 195   default:
 196      m_button1.SetColor(BLACK, GREEN);
 197      currentColor = 0;
 198      break;
 199   }
 200}
colorBtn.h
   1//----------------------------------------------------
   2// colorBtn.h : changed by r. wuppinger
   3//----------------------------------------------------
   4
   5#ifndef __COLORBTN_H__
   6#define __COLORBTN_H__
   7
   8/////////////////////////////////////////////////////////////////////////////
   9// colorBtn.h : header file for the CColorButton class
  10//
  11// Written by Bob Ryan (ryan@cyberzone.net)
  12// Copyright (c) 1998.
  13//
  14// This code may be freely distributable in any application.  If
  15// you make any changes to the source, please let me know so that
  16// I might make a better version of the class.
  17//
  18// This file is provided "as is" with no expressed or implied warranty.
  19// The author accepts no liability for any damage/loss of business that
  20// this product may cause.
  21//
  22/////////////////////////////////////////////////////////////////////////////
  23
  24const COLORREF CLOUDBLUE = RGB(128, 184, 223);
  25const COLORREF WHITE = RGB(255, 255, 255);
  26const COLORREF BLACK = RGB(1, 1, 1);
  27const COLORREF DKGRAY = RGB(128, 128, 128);
  28const COLORREF LTGRAY = RGB(192, 192, 192);
  29const COLORREF YELLOW = RGB(255, 255, 0);
  30const COLORREF DKYELLOW = RGB(128, 128, 0);
  31const COLORREF RED = RGB(255, 0, 0);
  32const COLORREF DKRED = RGB(128, 0, 0);
  33const COLORREF BLUE = RGB(0, 0, 255);
  34const COLORREF DKBLUE = RGB(0, 0, 128);
  35const COLORREF CYAN = RGB(0, 255, 255);
  36const COLORREF DKCYAN = RGB(0, 128, 128);
  37const COLORREF GREEN = RGB(0, 255, 0);
  38const COLORREF DKGREEN = RGB(0, 128, 0);
  39const COLORREF MAGENTA = RGB(255, 0, 255);
  40const COLORREF DKMAGENTA = RGB(128, 0, 128);
  41
  42
  43#define CB_BG_DEFAULT       LTGRAY
  44#define CB_FG_DEFAULT       BLACK            // black text
  45#define CB_SID_DEFAULT    DKGRAY          // dark gray disabled text
  46
  47
  48class CColorButton : public CButton
  49{
  50DECLARE_DYNAMIC(CColorButton)
  51public:
  52   CColorButton();
  53   virtual ~CColorButton();
  54
  55   BOOL Attach(const UINT nID, CWnd* pParent,
  56      const COLORREF BGColor = CB_BG_DEFAULT,    // gray button
  57      const COLORREF FGColor = CB_FG_DEFAULT,          // black text
  58      const COLORREF DisabledColor = CB_SID_DEFAULT,   // dark gray disabled text
  59      const UINT nBevel = 2
  60   );
  61
  62   void SetFGColor( COLORREF color = CB_FG_DEFAULT, BOOL bRedraw=FALSE) { m_fg = color; if(bRedraw)
  63InvalidateRect(NULL);}
  64   void SetBGColor( COLORREF color = CB_BG_DEFAULT, BOOL bRedraw=FALSE) {   m_bg = color;  if(bRedraw)
  65InvalidateRect(NULL);}
  66  void SetDisabledColor(COLORREF color = CB_SID_DEFAULT, BOOL bRedraw=FALSE) { m_disabled= color;
  67if(bRedraw) InvalidateRect(NULL);}
  68
  69   void SetColor( COLORREF colFG = CB_FG_DEFAULT,   COLORREF colBG= CB_BG_DEFAULT,   COLORREF colDIS =
  70CB_SID_DEFAULT, BOOL bRedraw=TRUE)
  71         { SetFGColor( colFG);
  72         SetBGColor( colBG);
  73       SetDisabledColor(colDIS);
  74            if(bRedraw) InvalidateRect(NULL);
  75         }
  76
  77protected:
  78   virtual void DrawItem(LPDRAWITEMSTRUCT lpDIS);
  79   void DrawFrame(CDC *DC, CRect R, int Inset);
  80   void DrawFilledRect(CDC *DC, CRect R, COLORREF color);
  81   void DrawLine(CDC *DC, CRect EndPoints, COLORREF color);
  82   void DrawLine(CDC *DC, long left, long top, long right, long bottom, COLORREF color);
  83   void DrawButtonText(CDC *DC, CRect R, const char *Buf, COLORREF TextColor);
  84
  85   COLORREF GetFGColor() { return m_fg; }   
  86   COLORREF GetBGColor() { return m_bg; }
  87   COLORREF GetDisabledColor() { return m_disabled; }
  88   UINT GetBevel() { return m_bevel; }
  89
  90private:
  91   COLORREF m_fg, m_bg, m_disabled;
  92   UINT m_bevel;
  93
  94};
  95#endif
colorBtn.cpp
   1
   2
   3//----------------------------------------------------
   4// colorBtn.cpp : changed by r. wuppinger
   5//----------------------------------------------------
   6
   7
   8
   9/////////////////////////////////////////////////////////////////////////////
  10// colorBtn.cpp : implementation file for the CColorButton class
  11//
  12// Written by Bob Ryan (ryan@cyberzone.net)
  13// Copyright (c) 1998.
  14//
  15// This code may be freely distributable in any application.  If
  16// you make any changes to the source, please let me know so that
  17// I might make a better version of the class.
  18//
  19// This file is provided "as is" with no expressed or implied warranty.
  20// The author accepts no liability for any damage/loss of business that
  21// this product may cause.
  22//
  23/////////////////////////////////////////////////////////////////////////////
  24
  25
  26#include "stdafx.h"
  27#include "colorbtn.h"
  28 
  29#ifdef _DEBUG
  30#undef THIS_FILE
  31static char BASED_CODE THIS_FILE[] = __FILE__;
  32#endif
  33
  34// no automatic class substitution for this file!
  35#ifdef CColorButton
  36#undef CColorButton      CColorButton
  37#endif
  38
  39// CColorButton
  40IMPLEMENT_DYNAMIC(CColorButton, CButton)
  41
  42CColorButton::CColorButton()
  43
  44#if (_MFC_VER < 0x0250)
  45  hwndOwner = NULL;  // initialize hwndOwner for GetOwner() and SetOwner() support in MFC < 2.5
  46#endif
  47}
  48
  49
  50
  51CColorButton::~CColorButton()
  52{
  53}
  54
  55
  56
  57BOOL CColorButton::Attach(const UINT nID, CWnd* pParent, const COLORREF BGColor, const COLORREF FGColor,
  58const COLORREF DisabledColor, const UINT nBevel)
  59{
  60   if (!SubclassDlgItem(nID, pParent))
  61      return FALSE;
  62
  63   m_fg = FGColor;
  64   m_bg = BGColor;
  65   m_disabled = DisabledColor;
  66   m_bevel = nBevel;
  67
  68   return TRUE;
  69}
  70
  71
  72void CColorButton::DrawItem(LPDRAWITEMSTRUCT lpDIS)
  73{
  74   CDC* pDC = CDC::FromHandle(lpDIS->hDC);
  75
  76   UINT state = lpDIS->itemState;
  77   CRect focusRect, btnRect;
  78   focusRect.CopyRect(&lpDIS->rcItem);
  79   btnRect.CopyRect(&lpDIS->rcItem);
  80
  81   //
  82   // Set the focus rectangle to just past the border decoration
  83   //
  84   focusRect.left += 4;
  85   focusRect.right -= 4;
  86   focusRect.top += 4;
  87   focusRect.bottom -= 4;
  88    
  89   //
  90   // Retrieve the button's caption
  91   //
  92   const int bufSize = 512;
  93   TCHAR buffer[bufSize];
  94   GetWindowText(buffer, bufSize);
  95   
  96
  97   //
  98   // Draw and label the button using draw methods
  99   //
 100   DrawFilledRect(pDC, btnRect, GetBGColor());
 101   DrawFrame(pDC, btnRect, GetBevel());
 102   DrawButtonText(pDC, btnRect, buffer, GetFGColor());
 103
 104
 105   //
 106   // Now, depending upon the state, redraw the button (down image) if it is selected,
 107   // place a focus rectangle on it, or redisplay the caption if it is disabled
 108   //
 109   if (state & ODS_FOCUS) {
 110      DrawFocusRect(lpDIS->hDC, (LPRECT)&focusRect);
 111      if (state & ODS_SELECTED){
 112         DrawFilledRect(pDC, btnRect, GetBGColor());
 113         DrawFrame(pDC, btnRect, -1);
 114
 115
 116// ----> Changes!   // changes by RW:
 117
 118            // move the button text if it is pressed...
 119            CRect rectPressedBtnText = btnRect;
 120            
 121            // to the right and bottom...
 122            rectPressedBtnText.OffsetRect( 1, 1 );
 123
 124            // ... and now paint it!
 125         DrawButtonText(pDC, rectPressedBtnText, buffer, GetFGColor());
 126
 127         // DrawButtonText(pDC, btnRect, buffer, GetFGColor());
 128         DrawFocusRect(lpDIS->hDC, (LPRECT)&focusRect);
 129      }
 130   }
 131   else if (state & ODS_DISABLED) {
 132      //COLORREF disabledColor = bg ^ 0xFFFFFF; // contrasting color
 133      DrawButtonText(pDC, btnRect, buffer, GetDisabledColor());
 134   }
 135}
 136
 137
 138void CColorButton::DrawFrame(CDC *DC, CRect R, int Inset)
 139{
 140   COLORREF dark, light, tlColor, brColor;
 141   int i, m, width;
 142   width = (Inset < 0)? -Inset : Inset;
 143   
 144   for (i = 0; i < width; i += 1) {
 145      m = 255 / (i + 2);
 146      dark = PALETTERGB(m, m, m);
 147      m = 192 + (63 / (i + 1));
 148      light = PALETTERGB(m, m, m);
 149       
 150      if ( width == 1 ) {
 151         light = RGB(255, 255, 255);
 152         dark = RGB(128, 128, 128);
 153      }
 154      
 155      if ( Inset < 0 ) {
 156         tlColor = dark;
 157         brColor = light;
 158      }
 159      else {
 160         tlColor = light;
 161         brColor = dark;
 162      }
 163      
 164      DrawLine(DC, R.left, R.top, R.right, R.top, tlColor);                  
 165     // Across top
 166      DrawLine(DC, R.left, R.top, R.left, R.bottom, tlColor);                
 167     // Down left
 168    
 169      if ( (Inset < 0) && (i == width - 1) && (width > 1) ) {
 170         DrawLine(DC, R.left + 1, R.bottom - 1, R.right, R.bottom - 1, RGB(1, 1, 1));// Across bottom
 171         DrawLine(DC, R.right - 1, R.top + 1, R.right - 1, R.bottom, RGB(1, 1, 1));   // Down right
 172      }
 173      else {
 174         DrawLine(DC, R.left + 1, R.bottom - 1, R.right, R.bottom - 1, brColor);    // Across bottom
 175         DrawLine(DC, R.right - 1, R.top + 1, R.right - 1, R.bottom, brColor);      // Down right
 176      }
 177      InflateRect(R, -1, -1);
 178   }
 179}
 180
 181
 182
 183void CColorButton::DrawFilledRect(CDC *DC, CRect R, COLORREF color)
 184{
 185   CBrush B;
 186   B.CreateSolidBrush(color);
 187   DC->FillRect(R, &B);
 188}
 189
 190
 191void CColorButton::DrawLine(CDC *DC, CRect EndPoints, COLORREF color)
 192{
 193   CPen newPen;
 194   newPen.CreatePen(PS_SOLID, 1, color);
 195   CPen *oldPen = DC->SelectObject(&newPen);
 196   DC->MoveTo(EndPoints.left, EndPoints.top);
 197   DC->LineTo(EndPoints.right, EndPoints.bottom);
 198   DC->SelectObject(oldPen);
 199   newPen.DeleteObject();
 200}
 201
 202void CColorButton::DrawLine(CDC *DC, long left, long top, long right, long bottom, COLORREF color)
 203{
 204   CPen newPen;
 205   newPen.CreatePen(PS_SOLID, 1, color);
 206   CPen *oldPen = DC->SelectObject(&newPen);
 207   DC->MoveTo(left, top);
 208   DC->LineTo(right, bottom);
 209   DC->SelectObject(oldPen);
 210   newPen.DeleteObject();
 211}
 212
 213
 214void CColorButton::DrawButtonText(CDC *DC, CRect R, const char *Buf, COLORREF TextColor)
 215{
 216   COLORREF prevColor = DC->SetTextColor(TextColor);
 217   DC->SetBkMode(TRANSPARENT);
 218   DC->DrawText(Buf, strlen(Buf), R, DT_CENTER|DT_VCENTER|DT_SINGLELINE);
 219   DC->SetTextColor(prevColor);
 220}
Copyright   2001 Dr. Christopher C. Taylor t a y l o r@m s o e.e d u Last updated: Fri Jun 14 14:06:30 2002