![]() |
Languages »
C / C++ Language »
General
Intermediate
License: The Code Project Open License (CPOL)
Vista Style Progress Bar in C#By XasthomA Vista style Progress Bar complete with color property and animation |
C#, VB 7.x, Windows, .NET 1.1, GDI+, VS.NET2003, Dev
|
|
Advanced Search Add to IE Search |
|
|
|
||||||||||||||||

This was supposed to be a short article on the progress bar I wrote but it rapidly turned into an essay. The stuff in this article isn't necessary to understand the code so don't feel you have to read it. Oh, and before I forget I'm going to apologize in advance for my coding style. I've never actually had a professional (or anyone at all) really look at anything I've done before, so the way I code might be really bad. I made a Smooth ProgressBar control in VB.NET about 7 months ago as a personal project to help further my knowledge in the GDI+. Since then I upgraded my computer to Vista and immediately decided that the new progress bar was better than mine. Not wanting to waste my original effort I decided to recreate the Windows Vista Progress Bar with some added features. I've always been annoyed at the lack of a colour property in the Windows Progress Bar controls from 2000, to Vista so that was the main reason for this control.
For me, the first step was figuring out how the progress bar was actually drawn. Already having experience with Photoshop, I thought I'd use Photoshop to create a similar effect and then translate the stages into code using the GDI+. If you don't use Photoshop or aren't quite comfortable with using it you can skip this section and just take my word for it as it isn't very easy to follow.
The first thing I did was to find a decent screenshot of the Vista progress bar. This was the best one I found. It's taken from a short article written by Daniel Moth and can be found here.
|
The Vista progress bar
|
From this I broke it down visually into separate layers.
| Now all I had to do was recreate the layers step by step. To create the background layer, all that has to be done is to create a 2px rounded rectangle with an outer stroke of (#B2B2B2) and an inner stroke of (#FFFFFF) except you might want to lower the opacity of this layer. | |
|
The background
|
|
| Now you want to add a black to transparent gradient at the left hand side about 10 pixels wide and change the opacity to about 15%, then duplicate it, flip it and add it to the other side. | |
|
The shadows
|
|
| The next part is really easy. Just create a solid rectangle for the bar, filled with either (#00D328) for the green progress bar, (#D20000) for the red bar or (#D2CA00) for the yellow bar. Alternatively you could just use your own color, (#00ABD3) will give you a nice blue. | |
|
The bar
|
|
| I know it's looking bad at the moment but I promise by the end it will look better. Using the same technique for the background shadow create a gradient at either end of the bar except make them 20 pixels wide instead of 10. | |
|
The bar with shadows
|
|
| Next we add the top and bottom highlights. These highlights are simply a white to 50% transparent gradient approximately 5 pixels high starting from just under the white inner stroke at the top. The opacity of this layer can be left or changed, it's up to you. I left it at 100%. After you're happy with the highlight, duplicate the layer, flip it vertically and put it at the bottom, just above the bottom white line. I'd recommend dropping the opacity of the bottom highlight to about 40% but this is up to you. | |
|
The completed bar
|
After learning how the progress bar could be made, the next step was to translate these steps into the GDI+. I found the easiest way to do this was to break it down into different procedures that represent the different layers. That way if I wanted to tweak one of the layers, I'd just have to comment out the bits I wanted to hide. By the end my paint event looked like this:
private void ProgressBar_Paint(object sender, PaintEventArgs e)
{
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
e.Graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
DrawBackground(e.Graphics);
DrawBackgroundShadows(e.Graphics);
DrawBar(e.Graphics);
DrawBarShadows(e.Graphics);
DrawHighlight(e.Graphics);
DrawInnerStroke(e.Graphics);
DrawGlow(e.Graphics);
DrawOuterStroke(e.Graphics);
}
The first two lines make the graphics object draw with a higher quality and the other lines represent the different layers. It might not be the most efficient way of doing things CPU-wise but it makes things a lot easier for the little adjustments you have to make to get things to look right. I'll only take a brief look at each of these procedures because the code itself is fully commented and has XML documentation with it (see C__Documentation_Proess.asp for more details). In this article I quite frequently mention colours, I happen to neglect the alpha value quite a lot so if I say black I probably mean some kind of semi-transparent gray.
private void DrawBackground(Graphics g)
{
Rectangle r = this.ClientRectangle; r.Width--; r.Height--;
GraphicsPath rr = RoundRect(r, 2, 2, 2, 2);
g.FillPath(new SolidBrush(this.BackgroundColor), rr);
}
The only notable thing in the DrawBackground procedure is the use of the RoundRect function. It takes a rectangle object and four integers and returns a rectangular graphics path with rounded corners. The radius of the corners are determined by the four integers starting in the top left going clockwise to the bottom left.
private void DrawBackgroundShadows(Graphics g)
{
Rectangle lr = new Rectangle(2, 2, 10, this.Height - 5);
LinearGradientBrush lg = new LinearGradientBrush
(lr, Color.FromArgb(30, 0, 0, 0),
Color.Transparent,
LinearGradientMode.Horizontal);
lr.X--;
g.FillRectangle(lg, lr);
Rectangle rr = new Rectangle(this.Width - 12, 2, 10, this.Height - 5);
LinearGradientBrush rg = new LinearGradientBrush(rr, Color.Transparent,
Color.FromArgb(20, 0, 0, 0),
LinearGradientMode.Horizontal);
g.FillRectangle(rg, rr);
}
This method creates two 10 pixel wide rectangles and fills them with a linear gradient that goes from a low opacity black to transparent making sure that each rectangle has the darker colour on the outside.
private void DrawBar(Graphics g)
{
Rectangle r = new Rectangle(1, 2, this.Width - 3, this.Height - 3);
r.Width = (int)(Value * 1.0F / (MaxValue - MinValue) * this.Width);
g.FillRectangle(new SolidBrush(GetIntermediateColor()), r);
}
The code here is pretty much the same as the code that draws the background but with two minor differences. The first is that it uses a normal rectangle as opposed to a rounded one and the second is that it uses the Value, MaxValue, MinValue and Width properties to calculate the width of the bar. There's also another custom function here called GetIntermediateColor(). This method calculates the color to make the bar based on the current percentage of the Value and the StartColor and EndColor properties.
private void DrawBarShadows(Graphics g)
{
Rectangle lr = new Rectangle(1, 2, 15, this.Height - 3);
LinearGradientBrush lg = new LinearGradientBrush
(lr, Color.White, Color.White,
LinearGradientMode.Horizontal);
ColorBlend lc = new ColorBlend(3);
lc.Colors = new Color[]
{Color.Transparent, Color.FromArgb(40, 0, 0, 0), Color.Transparent};
lc.Positions = new float[] {0.0F, 0.2F, 1.0F};
lg.InterpolationColors = lc;
lr.X--;
g.FillRectangle(lg, lr);
Rectangle rr = new Rectangle(this.Width - 3, 2, 15, this.Height - 3);
rr.X = (int)(Value * 1.0F / (MaxValue - MinValue) * this.Width) - 14;
LinearGradientBrush rg =
new LinearGradientBrush(rr, Color.Black,Color.Black,
LinearGradientMode.Horizontal);
ColorBlend rc = new ColorBlend(3);
rc.Colors = new Color[]
{Color.Transparent, Color.FromArgb(40, 0, 0, 0), Color.Transparent};
rc.Positions = new float[] {0.0F, 0.8F, 1.0F};
rg.InterpolationColors = rc;
g.FillRectangle(rg, rr);
}
This code is almost the same as the shadows for the background except these ones are larger. They also use a different gradient. It starts off transparent and gets a bit darker before it goes completely transparent. I did this because in the Vista progress bar it doesn't just go straight from black to transparent.
private void DrawHighlight(Graphics g)
{
Rectangle tr = new Rectangle(1, 1, this.Width - 1, 6);
GraphicsPath tp = RoundRect(tr, 2, 2, 0, 0);
g.SetClip(tp);
LinearGradientBrush tg = new LinearGradientBrush(tr, Color.White,
Color.FromArgb(128, Color.White),
LinearGradientMode.Vertical);
g.FillPath(tg, tp);
g.ResetClip();
Rectangle br = new Rectangle(1, this.Height - 8, this.Width - 1, 6);
GraphicsPath bp = RoundRect(br, 0, 0, 2, 2);
g.SetClip(bp);
LinearGradientBrush bg = new LinearGradientBrush(br, Color.Transparent,
Color.FromArgb(100, this.HighlightColor),
LinearGradientMode.Vertical);
g.FillPath(bg, bp);
g.ResetClip();
}
This procedure simply adds the low opacity white highlight at the top of the bar and the more transparent one at the bottom, again just some simple linear gradient drawing.
private void DrawInnerStroke(Graphics g)
{
Rectangle r = this.ClientRectangle;
r.X++; r.Y++; r.Width-=3; r.Height-=3;
GraphicsPath rr = RoundRect(r, 2, 2, 2, 2);
g.DrawPath(new Pen(Color.FromArgb(100, Color.White)), rr);
}
This bit draws the low opacity line on the inside of the border. It's not overly noticeable but it still helps to add that little flare.
private void DrawGlow(Graphics g)
{
Rectangle r = new Rectangle(mGlowPosition, 0, 60, this.Height);
LinearGradientBrush lgb = new LinearGradientBrush
(r, Color.White, Color.White,
LinearGradientMode.Horizontal);
ColorBlend cb = new ColorBlend(4);
cb.Colors = new Color[]
{Color.Transparent, this.GlowColor, this.GlowColor, Color.Transparent};
cb.Positions = new float[] {0.0F, 0.5F, 0.6F, 1.0F};
lgb.InterpolationColors = cb;
Rectangle clip = new Rectangle(1, 2, this.Width - 3, this.Height - 3);
clip.Width = (int)(Value * 1.0F / (MaxValue - MinValue) * this.Width);
g.SetClip(clip);
g.FillRectangle(lgb,r);
g.ResetClip();
}
This part draws the animated glow that goes across the bar. It just draws a linear gradient that draws it's X value from a private variable called mGlowPosition that is incremented by a timer. The pause between each 'pass' of the glow is caused by setting the X value to a minus number. I know it would have been more efficient to use another timer to enact the pause but I was being lazy at the time, and besides - from concept to product in two days isn't too bad.
private void DrawOuterStroke(Graphics g)
{
Rectangle r = this.ClientRectangle; r.Width--; r.Height--;
GraphicsPath rr = RoundRect(r, 2, 2, 2, 2);
g.DrawPath(new Pen(Color.FromArgb(178, 178, 178)), rr);
}
Same as the inner stroke but bigger.
![]() |
Using this is really just as easy as using any other control. Just add the ProgressBar.cs file into your solution and build it. Then select ProgressBar from the 'My User Controls' in the Toolbox. |
Setting the properties is pretty easy too. MinValue is the smallest value that the Value property will accept and MaxValue is it's maximum value. These should usually be left at their defaults. The Value property is the value that the bar length represents. The two properties for the bar are StartColor and EndColor. The StartColor is the colour of the bar when it's Value property is equal to MinValue. The EndColor property is the colour of the bar when it reaches the MaxValue.
The other properties are purely aesthetic and consist of the HighlightColor which changes the bottom highlight color, the BackgroundColor property sets the background for the bar. Then there's the Animate property, this determines whether the glow that runs across the bar is present. Finally there's the GlowColor property. This is the color of the glow that runs across the bar.
This bit is pretty much up to you guys, if anyone makes any valid suggestions, I'd be more than happy to implement them.
General
News
Question
Answer
Joke
Rant
Admin
|
PermaLink |
Privacy |
Terms of Use
Last Updated: 22 Jun 2007 Editor: Sean Ewington |
Copyright 2007 by Xasthom Everything else Copyright © CodeProject, 1999-2009 Web10 | Advertise on the Code Project |