Icon Progress Bar

Environment: [VC6]

This is a progress bar control that uses icons.

First create a static control of the required size in the dialog editor and set the style you want.

The 'Attach' function sets up the following features:

  • The icon to use in the progress bar.

  • The number of pixels to increment between each icon.

  • The background colour. (Optional, the default is the 3D face colour)

  • The stagger value. (Optional, the default is zero)
    This is the number of pixels that the icons will be offset each time, creating a zigzag pattern.

'Attach' will calculate the number of icons that can be contained within the static control and attempt to centralise the bar within it. It will derive the orientation of the bar by using the aspect ratio of the control.

'SetIconChange' allows you to modify the icon that will be displayed from a certain value.

Example

// ICON1 for the whole bar, increment of 4, pale 
// blue background, stagger of 2
Attach( IDC_ICON_PROGRESS, 
        this, 
      IDI_ICON1, 
      4, 
      RGB(128, 128, 255), 
      2);

SetRange(0, 100);
SetPos(0);

// Use icon 2 from position 10 to 100
SetIconChange(IDI_ICON2, 10);

// Use icon 3 from position 30 to 100
SetIconChange(IDI_ICON3, 30);

// Use icon 4 from position 70 to 100
SetIconChange(IDI_ICON4, 70);

Final result

0 - 9    = ICON1
10 - 29    = ICON2
30 - 69    = ICON3
70 - 100 = ICON4

The acuracy of the changeover position will depend on the number total of icons displayed for the range.

i.e.

If, in the above example, there were only 6 icons in total, the changeover position of 30 should occur at 1.78 icons. This of course can't happen so the changeover icon index will be rounded down to 1. This equates to a position of 17. The more icons you can fit into the bar the more accurate it will be.

CIconProgress supports all of the normal CProgressCtrl member functions.

///////////////////////////////////////////////
// CIconProgress window

class CIconProgress : public CWnd
{
// Construction
public:
   CIconProgress();

   void Attach(UINT control_id,
          CWnd *parent,
          UINT icon_id, 
          int  increment, 
          int  stagger = 0);

   void Attach(UINT     control_id,
          CWnd     *parent,
          UINT     icon_id, 
          int        increment, 
          COLORREF background,
          int        stagger = 0);

   void SetRange(short lower, 
            short upper);
   
   void SetRange32(int lower, 
         int upper);
   
   void GetRange(int &lower,
            int &upper);

   int  SetPos(int position);

   int  GetPos();
   
   int  OffsetPos(int offset);

   int  SetStep(int step);

   int  StepIt();

   void SetIconChange(UINT icon_id, int position);

// Attributes
public:

// Operations
public:

// Overrides
   // ClassWizard generated virtual function overrides
   //{{AFX_VIRTUAL(CIconProgress)
   //}}AFX_VIRTUAL

// Implementation
public:
   virtual      ~CIconProgress();

private:
   int      GetNumberOfIcons(int position); 

   static const int   HORIZONTAL;
   static const int   VERTICAL;

   CBrush      background_brush;
   HICON      hicon;
   COLORREF   background_colour;
   int      icon_increment;
   int      orientation;
   int      total_number_of_icons;
   int      number_of_icons;
   int      icon_major;
   int      icon_minor;
   int      window_major;
   int      window_minor;
   int      start_major;
   int      start_minor;
   int      stagger_distance;
   int      upper;
   int      lower;
   int      position;
   int      step;
   RECT      window_rect;

   CArray hicon_array;

   // Generated message map functions
protected:
   //{{AFX_MSG(CIconProgress)
   afx_msg void OnPaint();
   afx_msg BOOL OnEraseBkgnd(CDC* pDC);
   //}}AFX_MSG
   DECLARE_MESSAGE_MAP()
};

// Constants
const int CIconProgress::HORIZONTAL = 0;
const int CIconProgress::VERTICAL   = 1;

// Message map
BEGIN_MESSAGE_MAP(CIconProgress, CWnd)
   //{{AFX_MSG_MAP(CIconProgress)
   ON_WM_PAINT()
   ON_WM_ERASEBKGND()
   //}}AFX_MSG_MAP
END_MESSAGE_MAP()

//////////////////////////////////////////
// Constructor

CIconProgress::CIconProgress()
{
	upper    = 100;
	lower    = 0;
	position = 0;
	step     = 10;
}

//////////////////////////////////////////
// Destructor

CIconProgress::~CIconProgress()
{
   background_brush.DeleteObject();
}

//////////////////////////////////////////
// SetInitial - default colour

void CIconProgress::Attach( UINT control_id, 
                            CWnd *parent, 
                            UINT icon_id, 
                            int increment, 
                            int stagger)
{
   Attach( control_id, 
            parent, 
         icon_id, 
         increment, 
         GetSysColor(COLOR_3DFACE), 
         stagger);
}

//////////////////////////////////////////
// SetInitial - specified colour

void CIconProgress::Attach( UINT control_id, 
                            CWnd *parent, 
                            UINT icon_id, 
                            int increment, 
                            COLORREF background, 
                            int stagger)
{
   SubclassDlgItem(control_id, parent);

   // Store the parameters.
   icon_increment    = increment;
   background_colour = background;
   stagger_distance  = stagger;

   // Create the background brush.
   background_brush.CreateSolidBrush(background_colour);

   // Get the icon handle.
   hicon = AfxGetApp()->LoadIcon(icon_id);

   // Find the client window size.
   GetClientRect(&window_rect);

   // Set the orientation and the major and minor axis.
   // Major is in the direction of progress.
   if (window_rect.right > window_rect.bottom)
   {
      orientation = HORIZONTAL;
   }
   else
   {
      orientation = VERTICAL;
   }
   
   switch (orientation)
   {
      case HORIZONTAL:
      {
         icon_major   = GetSystemMetrics(SM_CXICON);
         icon_minor   = GetSystemMetrics(SM_CYICON);
         window_minor = window_rect.bottom;
         window_major = window_rect.right;
         break;
      }
   
      case VERTICAL:
      {
         icon_minor   = GetSystemMetrics(SM_CXICON);
         icon_major   = GetSystemMetrics(SM_CYICON);
         window_major = window_rect.bottom;
         window_minor = window_rect.right;
         break;
      }
   }

   // Adjust for daft icon increment.
   if (icon_increment == 0)
   {
      icon_increment = 1;
   }

   // Find the number of icons that will fit in the window.
   total_number_of_icons = 1;
   while (((total_number_of_icons * icon_increment)
            + icon_major) < window_major)
   {
      total_number_of_icons++;
   }

   // Fill the icon array with the default icon
   hicon_array.SetSize(total_number_of_icons);
   for (int i = 0; i < total_number_of_icons; i++)
   {
      hicon_array[i] = hicon;
   }

   // Find the start points.
   switch (orientation)
   {
      case HORIZONTAL:
      {
        // Left.
        start_major = (window_major - 
            (((total_number_of_icons - 1) 
           * icon_increment) 
           + icon_major)) / 2;
         break;
      }

      case VERTICAL:
      {
        // Bottom.
        start_major = ((window_major 
            + (((total_number_of_icons - 1) 
          * icon_increment) + icon_major)) / 2) 
          - icon_major;
      }
   }

   start_minor = (window_minor - 
                  icon_minor - 
               stagger_distance) / 2;
}

//////////////////////////////////////////
// SetPos

int CIconProgress::SetPos(int new_position)
{
   int old_position;
      
   old_position    = position;
   position        = new_position;
   position        = min(upper, max(lower, position)); 
   number_of_icons = GetNumberOfIcons(position);
   
   Invalidate();

   return old_position;
}

//////////////////////////////////////////
// OffsetPos

int CIconProgress::OffsetPos(int offset)
{
   int old_position;
   
   old_position    = position;
   position       += offset;
   position        = min(upper, max(lower, position)); 
   number_of_icons = GetNumberOfIcons(position);
   
   Invalidate();

   return old_position;
}

//////////////////////////////////////////
// SetRange

void CIconProgress::SetRange(short lower_range, 
                             short upper_range)
{
   SetRange32(lower_range, upper_range);
}

//////////////////////////////////////////
// SetRange32

void CIconProgress::SetRange32( int lower_range, 
                                int upper_range)
{
   lower           = lower_range;
   upper           = upper_range;
   position        = min(upper, max(lower, position)); 
   number_of_icons = GetNumberOfIcons(position);
   
   Invalidate();
}

//////////////////////////////////////////
// SetIconChange

void  CIconProgress::SetIconChange( UINT icon_id, 
                                    int position)
{
   HICON hicon = AfxGetApp()->LoadIcon(icon_id);

   // Where to start from
   int change_icon_index = GetNumberOfIcons(position);

   // Fill part of the array with the new icon
   for ( int i = change_icon_index; 
         i < total_number_of_icons; 
        i++)
   {
      hicon_array[i] = hicon;
   }
}

//////////////////////////////////////////
// SetStep

int CIconProgress::SetStep(int new_step)
{
   int old_step;
   
   old_step = step;
   step     = new_step;

   return old_step;
}

//////////////////////////////////////////
// StepIt

int CIconProgress::StepIt()
{
   int old_position;
   
   old_position    = position;
   position       += step;
   position        = min(upper, max(lower, position)); 
   number_of_icons = GetNumberOfIcons(position);
   
   Invalidate();

   return old_position;
}

//////////////////////////////////////////
// GetPos

int CIconProgress::GetPos()
{
   return position;
}

//////////////////////////////////////////
// GetRange

void CIconProgress::GetRange(int &lower_range, 
                             int &upper_range)
{
   lower_range = lower;
   upper_range = upper;
}

//////////////////////////////////////////
// GetNumberOfIcons

int CIconProgress::GetNumberOfIcons(int position)
{
   int   n_icons;
   float delta = (float)(position - lower);
   float range = (float)(upper - lower + 1);
   
   float range_per_icon = range / total_number_of_icons;

   // Just in case of rounding errors
   if (position == upper)
   {
      n_icons = total_number_of_icons;
   }
   else
   {
      n_icons = (int)(delta / range_per_icon);
   }

   return n_icons;
}

//////////////////////////////////////////
// CIconProgress message handlers

void CIconProgress::OnPaint() 
{
   CPaintDC dc(this);
   CDC      memdc;
   CBitmap    bitmap;
   int      x, y, count;
   BOOL    do_stagger = FALSE;

   // Create a memory copy to draw to.
   memdc.CreateCompatibleDC(&dc);
   bitmap.CreateCompatibleBitmap(&dc, 
                         window_rect.right, 
                       window_rect.bottom);
   memdc.SelectObject(&bitmap);

   // Clear the background.
   memdc.FillRect(&window_rect, &background_brush);
   
   // Draw the icons.
   switch (orientation)
   {
      case HORIZONTAL:
      {
         for ( x = start_major, 
               count = 0; count < number_of_icons; 
               x += icon_increment, count++)
         {
           if (do_stagger)
           {
             memdc.DrawIcon(x, 
                         start_minor + stagger_distance,
                       hicon_array[count]);
           }
           else
           {
             memdc.DrawIcon(x, start_minor, hicon_array[count]);
           }

           do_stagger = !do_stagger;
         }
         break;
      }

      case VERTICAL:
      {
         for ( y = start_major, count = 0; 
              count < number_of_icons;
              y -= icon_increment, count++)
         {
            if (do_stagger)
            {
               memdc.DrawIcon(start_minor + 
                              stagger_distance, 
                           y, 
                           hicon_array[count]);
            }
            else
            {
               memdc.DrawIcon(start_minor, 
                              y, 
                              hicon_array[count]);
            }

            do_stagger = !do_stagger;
         }
         break;
      }
   }

   // Copy it to the screen.
   dc.BitBlt( 0, 0, 
              window_rect.right, 
            window_rect.bottom, 
            &memdc, 
            0, 0, 
            SRCCOPY);

   // Delete the tempory resources.
   memdc.DeleteDC();
   bitmap.DeleteObject();
}

BOOL CIconProgress::OnEraseBkgnd(CDC* pDC) 
{
   // Indicate that it is already erased.
   return TRUE;
}

Downloads

Download demo project - 16Kb
Download source - 3Kb