Introduction
After searching for many hours for the bouncing control bar you see in XP, I found this seemingly necessary control missing. So never having created a custom control before, I set out to do just that. My experience in .NET is limited, my time developing the control was limited, but my enthusiasm was not. Here is the result.
Special thanks to Microsoft for their introduction in the article HOW TO: Create a Smooth Progress Bar in Visual C# .NET.
I will only go over the main points as I expect everyone is familiar with programming....
When we update the value (which is automatically done by a forms timer), we need to see if it has gone to the edge of our control. This is determined base on the style of progressbar you selected. When it has gone as far as it can go, it goes the other way.
if (increasing)
{
val++;
}
else
{
val--;
}
if (val < min)
{
val = min;
increasing = true;
}
else if ((style == LocTecBarStyle.SmoothBounce) && (val > (max - blockWidth)))
{
val = (max - blockWidth);
increasing = false;
}
else if ((style == LocTecBarStyle.BlockBounce) &&
(val > (max - (blockWidth * 2) - blockSpace)))
{
val = (max - (blockWidth * 2) - blockSpace);
increasing = false;
}
Next, we set up the area to invalidate. This is to minimize flickering. For a smooth progress bar we only invalidate a total width of two pixels. For the block style we invalidate a width of four pixels. This causes Windows to redraw those areas.
Rectangle invalidateLeftArea = this.ClientRectangle;
Rectangle invalidateRightArea = this.ClientRectangle;
Rectangle invalidateInnerLeftArea = this.ClientRectangle;
Rectangle invalidateInnerRightArea = this.ClientRectangle;
invalidateLeftArea.Width = 1;
invalidateRightArea.Width = 1;
invalidateInnerLeftArea.Width = 1;
invalidateInnerRightArea.Width = 1;
if (increasing)
{
invalidateLeftArea.X = (val - 1);
invalidateRightArea.X = (val + blockWidth - 1);
if (style == LocTecBarStyle.BlockBounce)
{
invalidateRightArea.X = (val + (blockWidth * 2) + blockSpace - 1);
invalidateInnerLeftArea.X = (val + blockWidth - 1);
invalidateInnerRightArea.X = (val + blockWidth + blockSpace - 1);
}
}
else
{
invalidateRightArea.X = (val + blockWidth);
invalidateLeftArea.X = (val);
if (style == LocTecBarStyle.BlockBounce)
{
invalidateRightArea.X = (val + (blockWidth * 2) + blockSpace);
invalidateInnerRightArea.X = (val + blockWidth + blockSpace);
invalidateInnerLeftArea.X = (val + blockWidth);
}
}
this.Invalidate(invalidateLeftArea);
this.Invalidate(invalidateRightArea);
if (style == LocTecBarStyle.BlockBounce)
{
this.Invalidate(invalidateInnerLeftArea);
this.Invalidate(invalidateInnerRightArea);
}
In the OnPaint
event, we draw the rectangles. For the block style, we use the gradient for a true XP feel. There is a problem with it where (depending on the height of the control) it sometimes leaves a one pixel line between the gradients. If you know why, let me know. If I find out why, I will post the update.
Graphics g = e.Graphics;
SolidBrush brush = new SolidBrush(BarColor);
SolidBrush spaceBrush = new SolidBrush(this.BackColor);
float percent = (float)(val - min) / (float)(max - min);
switch (style)
{
case (LocTecBarStyle.SmoothBounce):
{
Rectangle rect = this.ClientRectangle;
rect.Width = blockWidth;
rect.X = val;
g.FillRectangle(brush, rect);
break;
}
case (LocTecBarStyle.BlockBounce):
{
Rectangle gradientTop = this.ClientRectangle;
Rectangle gradientBottom = this.ClientRectangle;
Rectangle space = this.ClientRectangle;
gradientTop.Width = (blockWidth * 2) + blockSpace;
gradientBottom.Width = (blockWidth * 2) + blockSpace;
space.Width = blockSpace;
gradientTop.Height = (int)(ClientRectangle.Height * .5) - 1;
gradientBottom.Height = ClientRectangle.Height - gradientTop.Height;
gradientTop.Y = ClientRectangle.Y;
gradientBottom.Y = gradientTop.Bottom;
gradientTop.X = val;
gradientBottom.X = val;
space.X = val + blockWidth;
LinearGradientMode lgmTop = LinearGradientMode.Vertical;
using (LinearGradientBrush lgbTop =
new LinearGradientBrush(gradientTop, this.BackColor, BarColor, lgmTop))
g.FillRectangle(lgbTop, gradientTop);
LinearGradientMode lgmBottom = LinearGradientMode.Vertical;
using (LinearGradientBrush lgbBottom =
new LinearGradientBrush(gradientBottom, BarColor, this.BackColor, lgmBottom))
g.FillRectangle(lgbBottom, gradientBottom);
g.FillRectangle(spaceBrush, space);
break;
}
}
Draw3DBorder(g);
brush.Dispose();
g.Dispose();
I have not optimized, perfected nor managed properly this code. I am open to criticisms, opinions, ideas, suggestions, etc. One last mention. If you are performing an intense task you will want to put that task in a separate thread so that this control can update its interface.
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.