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

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read