Click here to Skip to main content
Email Password   helpLost your password?
pbarWithPercentageDemo.gif pbarWithPercentageDemo.gif

Introduction

This class extends the native .NET progress bar with a percentage property that can be displayed inside the progress bar, and is used to get or set the value of the bar itself. The text can be aligned with the Alignment property and also has Font and Padding properties to allow further customization. On top of this, you can change the color of the percentage that is in the colored part of the progress bar.

Why ANOTHER progress bar control, you might ask yourself. Simple. Before creating this control, I did quite some research into finding a control that enabled displaying a percentage in a progress bar. Most of them succeeded in doing this with varying degrees of customization options, and worked fine in Windows XP. However, once using it on a Vista, the percentage would get deleted because the bar continuously redraws itself. This control will automatically redraw the percentage when the bar is refreshed.

Background

This class contains a lot of code based upon work by people on VB.NET forums. Go to this thread for more information. I am using this component in multiple VB.NET applications without any problems, and it should work in projects in other .NET languages.

Using the Code

Once you added the class to your project, you should be able to add the new component to your forms with the designer or do it manually with code.

Here you have an overview of properties that have been added:

<Browsable(True), Category("Appearance"), _
	Description("The percentage of the progressbar")> _
Public Property Percentage() As Double
    Get
        Return Me.Value / Me.Maximum * 100
    End Get
    Set(ByVal value As Double)
        If value >= 0 And value <= 100 Then
            Me.Value = CInt(Me.Maximum * value / 100)
            If Me.PercentageVisible And Me.AutoUpdatePercentage _
			Then Me.ShowPercentage()
        End If
    End Set
End Property
<Browsable(True), Category("Appearance"), DefaultValue(0), _
	Description("Gets or sets the amount of decimals that will _
	be displayed in the percentage")> _
Public Overridable Property PercentageDecimals() As Int32
    Get
        Return m_decimals
    End Get
    Set(ByVal value As Int32)
        If value > -1 Then m_decimals = value
    End Set
End Property
<Browsable(True), Category("Appearance"), DefaultValue(True), _
	Description("Gets or sets if the percentage should be visible")> _
Public Overridable Property PercentageVisible() As Boolean
    Get
        Return m_p_visible
    End Get
    Set(ByVal value As Boolean)
        If value <> Me.PercentageVisible Then
            If Not value Then Me.Graphics.Clear(Color.Transparent)
            m_p_visible = value
            RaiseEvent PercentageVisibleChanged(Me, New EventArgs)
        End If
    End Set
End Property
<Browsable(True), Category("Appearance"), DefaultValue("MiddleCenter"), _
	Description("Gets or sets if the percentage alignment")> _
Public Overridable Property PercentageAlign() As ContentAlignment
    Get
        Return m_p_align
    End Get
    Set(ByVal value As ContentAlignment)
        m_p_align = value
    End Set
End Property
<Browsable(True), Category("Appearance"), _
	Description("Gets or sets the color of the percentage text _
	at the place of the progressbar that is indicated")> _
Public Overridable Property OverLayColor() As Color
    Get
        Return m_overLayFont
    End Get
    Set(ByVal value As Color)
        m_overLayFont = value
    End Set
End Property
<Browsable(True), Category("Behavior"), DefaultValue(True), _
	Description("Gets or sets if the percentage should be auto updated")> _
Public Overridable Property AutoUpdatePercentage() As Boolean
    Get
        Return m_auto_update
    End Get
    Set(ByVal value As Boolean)
        m_auto_update = value
    End Set
End Property
<Browsable(True), Category("Layout"), _
	Description("Gets or sets if the interior spacing of the control")> _
Public Overridable Overloads Property Padding() As Padding
    Get
        Return MyBase.Padding
    End Get
    Set(ByVal value As Padding)
        MyBase.Padding = value
    End Set
End Property
<Browsable(True), Category("Appearance"), _
	Description("Gets or sets the font of the percentage text")> _
Public Overridable Overloads Property Font() As Font
    Get
        Return MyBase.Font
    End Get
    Set(ByVal value As Font)
        MyBase.Font = value
    End Set
End Property

Example

A simple example of setting a few properties:

With Me.ProgressbarWithPercentage1
    .ForeColor = Drawing.Color.DimGray ' Color of the text not in the colored part
    .OverLayColor = Drawing.Color.Black ' Color of the text in the colored part
    .PercentageAlign = Drawing.ContentAlignment.MiddleLeft ' Alignment of the text
    .Padding = New Windows.Forms.Padding(20, 0, 0, 0) ' Padding of the text
End With

progbarwithpercentage.gif

Inside the Component

Now how does it actually work? I'll try to give you a reasonable understanding.

The most important method is the ShowText method, which draws a string onto the progress bar. This method can be used to draw any string, but is internally only used to draw the percentage.

Public Sub ShowText(ByVal text As String)
    ' Determine the areas for the ForeColor and OverlayColor
    Dim r1 As RectangleF = Me.ClientRectangle
    r1.Width = CSng(r1.Width * Me.Value / Me.Maximum)
    Dim reg1 As New Region(r1)
    Dim reg2 As New Region(Me.ClientRectangle)
    reg2.Exclude(reg1)

    ' Draw the string
    Me.Graphics.Clip = reg1
    Me.Graphics.DrawString(text, Me.Font, New SolidBrush(Me.OverLayColor), _
		Me.DrawingRectangle, m_strFormat)
    Me.Graphics.Clip = reg2
    Me.Graphics.DrawString(text, Me.Font, New SolidBrush(Me.ForeColor), _
	Me.DrawingRectangle, m_strFormat)

    reg1.Dispose()
    reg2.Dispose()
End Sub

This method is used by the ShowPercentage method in a pretty straight forward way:

Public Sub ShowPercentage()
    Me.ShowText(Math.Round(Me.Percentage, _
	Me.PercentageDecimals).ToString & "%")
End Sub

The rectangle to draw in is determined every time the control is resized and every time the padding is changed with the following code:

Private Sub setDrawingRectangle()
    ' Determine the coordinates and size of the drawing rectangle
	' depending on the progress bar size and padding
    Me.DrawingRectangle = New RectangleF(Me.Padding.Left, _
                                       Me.Padding.Top, _
                                       Me.Width - Me.Padding.Left - Me.Padding.Right, _
                                       Me.Height - Me.Padding.Top - Me.Padding.Bottom)
        End Sub

Note that this property is used for every draw event. Another property used for every draw event is the stringformat. This is set every time you change the alignment.

Private Sub setStringFormat()
    ' Determine the horizontal alignment
    Select Case Me.PercentageAlign
        Case ContentAlignment.BottomCenter, _
		ContentAlignment.BottomLeft, ContentAlignment.BottomRight
                    m_strFormat.LineAlignment = StringAlignment.Far
        Case ContentAlignment.MiddleCenter, _
		ContentAlignment.MiddleLeft, ContentAlignment.MiddleRight
                    m_strFormat.LineAlignment = StringAlignment.Center
        Case ContentAlignment.TopCenter, _
		ContentAlignment.TopLeft, ContentAlignment.TopRight
                    m_strFormat.LineAlignment = StringAlignment.Near
    End Select

    ' Determine the vertical alignment
    Select Case Me.PercentageAlign
        Case ContentAlignment.BottomLeft, _
		ContentAlignment.MiddleLeft, ContentAlignment.TopLeft
                    m_strFormat.Alignment = StringAlignment.Near
        Case ContentAlignment.BottomCenter, _
		ContentAlignment.MiddleCenter, ContentAlignment.TopCenter
                    m_strFormat.Alignment = StringAlignment.Center
        Case ContentAlignment.BottomRight, _
		ContentAlignment.MiddleRight, ContentAlignment.TopRight
                    m_strFormat.Alignment = StringAlignment.Far
    End Select
End Sub

History

Changes in version 1.0.10

Changes in version 1.0.9

Changes in version 1.0.6

Changes in version 1.0.5

Changes in version 1.0.4

You must Sign In to use this message board.
 
 
Per page   
 FirstPrevNext
GeneralXP BUG
bn2vs
3:57 29 Mar '09  
Hey guys,

I screwed something up for XP in the last version; the percentage won't be displayed properly. I'll fix that in a new version soon, but if you need this control now for XP, you can always use version 1.0.6: http://code.bn2vs.com/viewtopic.php?p=468#468[^].

Cheers
BN

My little forums: http://code.bn2vs.com

70 72 6F 67 72 61 6D 6D 69 6E 67 20 34 20 6C 69 66 65!

GeneralRe: XP BUG
rootjumper
21:22 1 Nov '09  
hi, invalidate your control before you call ShowText to prevent the bug Smile

greetz
GeneralRe: XP BUG
jeroen de dauw
2:46 2 Nov '09  
Hey,

I'll give that a try Smile

Cheers!

Jeroen De Dauw
---
Forums ; Blog ; Wiki
---
70 72 6F 67 72 61 6D 6D 69 6E 67 20 34 20 6C 69 66 65!

GeneralRe: XP BUG
jeroen de dauw
7:15 2 Nov '09  
Hey,

I apparently already fixed this a while back, but never released my last version of this component.

I've tweaked it a little more, and put it onto SourceForge (http://pprogressbar.svn.sourceforge.net/viewvc/pprogressbar/ProgressBarWithPercentage/[^]). I've also send an update request to TCP.

I'm not 100% sure the bug is fixed though. If I run it in compatibility mode for XP, it works alight, but a confirmation from someone using XP would be awesome Smile

Cheers!

Jeroen De Dauw
---
Forums ; Blog ; Wiki
---
70 72 6F 67 72 61 6D 6D 69 6E 67 20 34 20 6C 69 66 65!

GeneralAnother Thank You
Dean Slindee
17:40 18 Mar '09  
Just what I wanted. Any chance that a future update could include a ForeColor property?
Not sure, but XP Themes might override any color property you would set back to default Green.
GeneralRe: Another Thank You
bn2vs
6:57 20 Mar '09  
Hey,

The current version already includes a ForeColor property. Smile
Is this not working in your implementation? (If this is the case I'll have a closer look at it)

Cheers
BN

My little forums: http://code.bn2vs.com

70 72 6F 67 72 61 6D 6D 69 6E 67 20 34 20 6C 69 66 65!

GeneralRe: Another Thank You
dslindee
15:44 20 Mar '09  
The ForeColor property is for the percentage amount, so I misspoke. I would like to change the ProgressColor(?). In Vista, the disk drive utilization "progress" color is blue. I have also seen MS's ProgressBar momentarily change from Green to Red. But I don't see a property on the MS ProgressBar to set either. Again, this color may be overwhelmed by XP Themes, but one can hope.
GeneralRe: Another Thank You
bn2vs
18:51 21 Mar '09  
Hey,

I indeed did not add a property with that functionality. As far as I know the native .net component does not have it, although it is quite possible I just don't know it. I'll have a look into this, but won't add my own drawing methods to the component, cause I want to keep this class as simple as possible and focused on it's main goal: adding a percentage. There are a few progress bars here on the codeporject already that are build from scratch and where you can set the 'progress color' Smile

Btw, if you find anything, plz let me know and I'll try to implement if possible Smile

Cheers
BN

My little forums: http://code.bn2vs.com

70 72 6F 67 72 61 6D 6D 69 6E 67 20 34 20 6C 69 66 65!

GeneralThank you!
akirilov
6:34 12 Mar '09  
Very clear and easy to read/understand layout. Thank you for this article.

I don't use VB or C#, but I like the idea of drawing the string (ShowString) and by using Regions you are not bind to a certain figure (Rectangle). I think some eye candy effects could be done.
GeneralRe: Thank you!
bn2vs
10:10 12 Mar '09  
I'm happy to contribute this little component to the open source world Smile

If you have any suggestions, please don't hesitate to post them.

My little forums: http://code.bn2vs.com


70 72 6F 67 72 61 6D 6D 69 6E 67 20 34 20 6C 69 66 65!

Generalmanifest missing
rspercy58
0:04 12 Mar '09  
I cant compile your project because of a manifest certificate missing in the certificate store.

rspercy
1 + 1 = 186,440....Depending on the species.

GeneralRe: manifest missing
bn2vs
10:11 12 Mar '09  
What version of VS are you using? I tried the source project and it worked just fine.

My little forums: http://code.bn2vs.com


70 72 6F 67 72 61 6D 6D 69 6E 67 20 34 20 6C 69 66 65!

GeneralRe: manifest missing
rspercy58
13:35 12 Mar '09  
Visual Studio 2008 Pro Edition, Fully Updated

rspercy
1 + 1 = 186,440....Depending on the species.

GeneralRe: manifest missing
bn2vs
22:54 12 Mar '09  
Weird, I'm also using VS2008 Pro, and I can run it without any problems.

Just tried it again:
- downloaded
- unachieved
- opened the solution
- run the demo

Cheers
BN

My little forums: http://code.bn2vs.com


70 72 6F 67 72 61 6D 6D 69 6E 67 20 34 20 6C 69 66 65!

GeneralRe: manifest missing
rspercy58
0:48 13 Mar '09  
Deleted the old one, Re-Downloaded the progressBar, Unzipped it, Opened it up, 6Errors and 11 warnings
That is what I get. The major error is something that has todo with a console app. The other 5 errors
ProgressBarWithPercentage1.Value = TrackBar1.Value......
all these lines 1 - 5 all have errors....I know they should not have because there is nothing wrong with the code. I do not know what todo about the missing manifest file. Its not in the zip.

rspercy
1 + 1 = 186,440....Depending on the species.

GeneralRe: manifest missing
bn2vs
13:23 13 Mar '09  
Have you tried rebuilding the project?

Another thing that might be causing this is option strict or explicit. I have them on default in my project, so if you set them both to on this might cause the errors.

Anyway, I've just send a new version of this article + new demo and source files to TCP submit, so hopefully you won't have problems with that version Smile

Cheers
BN

My little forums: http://code.bn2vs.com


70 72 6F 67 72 61 6D 6D 69 6E 67 20 34 20 6C 69 66 65!

GeneralRe: manifest missing
rspercy58
16:22 13 Mar '09  
I always keep Strict and Explicit ON all the time. I did try to rebuild it too. I deleted the project and unzipped it a second time just to make sure it was not me. Same results. Ill try the new version and Ill get back to you. This seems like a good component if I can get it to compile.

Regards
rspercy58

rspercy
1 + 1 = 186,440....Depending on the species.

GeneralRe: manifest missing
rspercy58
16:56 13 Mar '09  
Got it working. Took the class file and added it to a new program and it worked fine. Thanx alot for this code. I have already learned alot from this alone.

Regards:
rspercy58

P.S. Disregard the last message.

rspercy
1 + 1 = 186,440....Depending on the species.

GeneralGraphics object.
DaveyM69
10:59 9 Mar '09  
Hi, I use C# not VB so I've been doing a simple port of the code. I notice the graphics object is changed in the constuctor, in OnHandleCreated and when handling SizeChanged (an unused method anyway!), and it is disposed in both Dispose and OnHandleDisposed.

Is there any reason for this? Why not create it and dispose of it in ShowPercentage?

I've found that the Value property shadow isn't needed at all. It also seems that the only place that needs to call ShowPercentage is from WndProc, as the change of any property seems to automatically cause the control to repaint. This makes quite a bit of the code redundant.

Also, if the Style is set to Marquee, then the overlay should probably be used for all the text regardless of the controls Value.

This aren't criticisms. There may be reasons why you've done it this way which I'd like to understand before I commot my C# implementation. Smile

Dave
BTW, in software, hope and pray is not a viable strategy. (Luc Pattyn)
Visual Basic is not used by normal people so we're not covering it here. (Uncyclopedia)

GeneralRe: Graphics object. [modified]
bn2vs
9:11 10 Mar '09  
Hey,

It is indeed possible to create the graphics object again every time you need to draw something, but I've chosen here to only create it when it's needed. This is a simple processor usage vs memory trade-off. I'm not sure this has been a good choice though. Can someone confirm the best way to solve this problem?

If you do not set the graphics object again every time the component is resized, it will keep the original size, and only occupy the first half of a bar with 200 width when the original was 100.

Your right that in most cases it is sufficient to call ShowPercentage from WndProc, but I don't think this is the case when working with a large interval (max value - min value) and change the value only a little. Would need to test this on a win XP to be sure though.

I did not put in any support for marquee yet, if you want to add this in your own implementation, sure do!

If you post your c# code I'll have a good look at it and maby include some stuff in the next version Smile

Cheers
BN
GeneralRe: Graphics object.
DaveyM69
3:39 11 Mar '09  
Hmm.. I think personally the simplicity of creating/disposing the graphics object each time would be preferable in this situation, progress bars are normally intended for reasonably long operations so any performance hit by creating the grapics each time should be unnoticeable. It's the first thing I changed in the code, which is why I didn't need the resize. Every resize sends WM_PAINT which calls the drawing method, where I create the grapics.

You're right about the call when the value changes. I was working on Vista where it sends WM_PAINT on every value change. Testing under XP, it seems that WM_PAINT is (understandably) only sent each time the progress bar needs to draw another bar, so intermediate value changes do not send the message. With the graphics in ShowPercentage, I'm having to call Invalidate there to force a repaint.

I'm actually combining a progress bar with other stuff to provide something totally different, but I'll post the progress bar part here when done. If I make an article from what I'm doing (probably will), then I'll link back to here for the progress bar part and make sure ful credit is given Big Grin

Dave
BTW, in software, hope and pray is not a viable strategy. (Luc Pattyn)
Visual Basic is not used by normal people so we're not covering it here. (Uncyclopedia)

GeneralStripped C# port [modified]
DaveyM69
5:14 11 Mar '09  
In my implementation I didn't need Padding, and just wanted the text center aligned vertically and horizontally, so I haven't used those parts. I've moved the Graphics so it's only Created/Disposed in the ShowPercentage method as discussed above. Hope someone finds it useful!
/* Original by By De Dauw Jeroen - jeroendedauw@gmail.com
* http://www.codeproject.com/KB/progress/progressbar-percentage.aspx */


using System;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;

namespace WinForms.Controls
{
/// <summary>
/// Control that extends the System.Windows.Forms.ProgressBar
/// with the ability to overlay the percentage.
/// </summary>
[Description(
"Control that extends the System.Windows.Forms.ProgressBar with the ability to overlay the percentage."),
DefaultProperty("PercentageVisible"),
DefaultEvent("PercentageVisibleChanged")]
public class ProgressBarWithPercentage : ProgressBar
{
private const int WM_PAINT = 0x0F;

/// <summary>
/// Raised when the visibility of the percentage text is changed.
/// </summary>
[Description("Raised when the visibility of the percentage text is changed."),
Category("Property Changed")]
public event EventHandler PercentageVisibleChanged;

private Color overlayColor;
private bool percentageVisible;
private StringFormat stringFormat;

/// <summary>
/// Create a new instance of a ProgressbarWithPercentage.
/// </summary>
public ProgressBarWithPercentage()
{
overlayColor = Color.White;
stringFormat = new StringFormat();
percentageVisible = true;
stringFormat.Alignment = StringAlignment.Center;
stringFormat.LineAlignment = StringAlignment.Center;
}

/// <summary>
/// Gets or sets the Color that is used to draw the text over a filled section of the progress bar.
/// </summary>
[Description("The Color that is used to draw the text over a filled section of the progress bar."),
Category("Appearance"),
DefaultValue(typeof(Color), "White")]
public Color OverlayColor
{
get { return overlayColor; }
set
{
if (overlayColor != value)
overlayColor = value;
}
}

/// <summary>
/// Gets or sets a value that indicates whether the percentage will be displayed.
/// </summary>
[Description("Indicates whether the percentage will be displayed on the progress bar."),
Category("Appearance"),
DefaultValue(true)]
public bool PercentageVisible
{
get { return percentageVisible; }
set
{
if (percentageVisible != value)
{
percentageVisible = value;
OnPercentageVisibleChanged(EventArgs.Empty);
}
}
}

public new int Value
{
get { return base.Value; }
set
{
if (base.Value != value)
{
base.Value = value;

/* Needed for XP. Downside is control will be drawn twice
* when value coincides with one that the system uses for
* repaint. Could maybe use Environment.OSVersion to check? */

if (percentageVisible)
Invalidate();
}
}
}

public override string Text
{
get
{
return Value.ToString() + "%";
}
}

protected virtual void OnPercentageVisibleChanged(EventArgs e)
{
EventHandler eh = PercentageVisibleChanged;
if (eh != null)
eh(this, e);
}

private void ShowPercentage()
{
using (Graphics graphics = CreateGraphics())
{
// Draw left side
Region regionLeft = new Region(new RectangleF(
ClientRectangle.X,
ClientRectangle.Y,
(ClientRectangle.Width * base.Value) / 100,
ClientRectangle.Height));
using (Brush brush = new SolidBrush(overlayColor))
{
graphics.Clip = regionLeft;
graphics.DrawString(Text, Font, brush, ClientRectangle, stringFormat);
}
// Draw right side
Region regionRight = new Region(ClientRectangle);
regionRight.Exclude(regionLeft);
using (Brush brush = new SolidBrush(ForeColor))
{
graphics.Clip = regionRight;
graphics.DrawString(Text, Font, brush, ClientRectangle, stringFormat);
}
}
}

protected override void WndProc(ref Message m)
{
base.WndProc(ref m);
if (percentageVisible && m.Msg == WM_PAINT)
ShowPercentage();
}
}
}


Dave
BTW, in software, hope and pray is not a viable strategy. (Luc Pattyn)
Visual Basic is not used by normal people so we're not covering it here. (Uncyclopedia)

modified on Wednesday, March 11, 2009 11:09 AM

GeneralRe: Stripped C# port
bn2vs
6:25 11 Mar '09  
Nice implementation Smile

I've linked it in the last version of this class as 'C#.Net implementation by DaveyM69'.

Going to wait a while before I update this article though, probably find some more things I can change Smile

Cheers
BN

My little forums: http://code.bn2vs.com


70 72 6F 67 72 61 6D 6D 69 6E 67 20 34 20 6C 69 66 65!


Last Updated 3 Nov 2009 | Advertise | Privacy | Terms of Use | Copyright © CodeProject, 1999-2010