Click here to Skip to main content
6,597,576 members and growing! (20,609 online)
Email Password   helpLost your password?
Languages » C / C++ Language » General     Intermediate License: The Code Project Open License (CPOL)

Vista Style Progress Bar in C#

By Xasthom

A Vista style Progress Bar complete with color property and animation
C#, VB 7.x, Windows, .NET 1.1, GDI+, VS.NET2003, Dev
Posted:22 Jun 2007
Views:76,016
Bookmarked:116 times
Announcements
Loading...
 
Search    
Advanced Search
Add to IE Search
printPrint   add Share
      Discuss Discuss   Broken Article?Report  
67 votes for this article.
Popularity: 8.64 Rating: 4.73 out of 5

1
1 vote, 1.5%
2
1 vote, 1.5%
3
11 votes, 16.7%
4
53 votes, 80.3%
5

Screenshot - Screenshot.jpg

Contents

Introduction

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.

From Photoshop...

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.

Screenshot - Vista.jpg
The Vista progress bar

From this I broke it down visually into separate layers.

  • Highlights
  • Shadows
  • Bar
  • Shadows
  • Background
Screenshot - Background.jpg 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
Screenshot - BGShadow.jpg 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
Screenshot - Bar.jpg 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
Screenshot - BarShadow.jpg 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
Screenshot - Finished.jpg 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

... To GDI+

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 the code

Screenshot - Toolbox.jpg 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.

Future Development

This bit is pretty much up to you guys, if anyone makes any valid suggestions, I'd be more than happy to implement them.

History

  • Version 1.0 (22 June 2007) - Initial Release

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Xasthom


Member
Hi, my name's Tom I'm a student in the UK and I'm 18 years old. I started programming when I was 11 with VB6 and now my primary language is C#. My other hobbies include Photoshop and playing the piano.
Location: United Kingdom United Kingdom

Other popular C / C++ Language articles:

Article Top
You must Sign In to use this message board.
FAQ FAQ 
 
Noise Tolerance  Layout  Per page   
 Msgs 1 to 23 of 23 (Total in Forum: 23) (Refresh)FirstPrevNext
GeneralCongrats + Memory Leak Note Pinmemberchrismaddern111:42 14 Jun '09  
GeneralWell done Pinmembercornedp2:55 4 Jun '09  
GeneralI love it Pinmemberdiscretion7:48 15 May '09  
GeneralProblem PinmemberPayam Rastogi3:40 11 Feb '09  
GeneralGreat job but bug using Animation PinmemberWilliam F6:32 25 Nov '08  
GeneralBug PinmemberZ-logic6:19 21 Oct '08  
GeneralGlow Timer suggestion Pinmembermichaelrterry5:22 29 Aug '08  
GeneralRoundRect - nice approach Pinmembervertex_x0:19 22 Aug '08  
GeneralReally nice PinmemberFredrik Bornander3:58 23 Nov '07  
GeneralRe: Really nice PinmemberXasthom9:05 23 Nov '07  
GeneralSimilar control PinmemberOwfAdmin1:52 18 Nov '07  
Generalnice approach PinmemberSunil Soni21:33 18 Jul '07  
GeneralVery Nice PinmemberPaul Conrad8:58 14 Jul '07  
GeneralDrawBar routine adjustment PinmemberJared James Sullivan17:13 6 Jul '07  
GeneralUse the using statement... PinmemberLukasz Swiatkowski6:55 26 Jun '07  
GeneralRe: Use the using statement... Pinmemberrepque12:02 16 May '08  
GeneralCool job Pinmemberanilmr3:44 26 Jun '07  
GeneralNice job Pinmemberpaillave0:26 25 Jun '07  
GeneralRe: Nice job Pinmemberxasthom0:54 25 Jun '07  
GeneralExcellent effort Pinmemberstewartwood18:51 23 Jun '07  
GeneralNice, Nice PinmemberJared James Sullivan18:36 23 Jun '07  
GeneralNice PinmemberAmr Elsehemy®9:45 23 Jun '07  
GeneralRe: Nice Pinmemberxasthom12:25 23 Jun '07  

General General    News News    Question Question    Answer Answer    Joke Joke    Rant Rant    Admin 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