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 .OverLayColor = Drawing.Color.Black .PercentageAlign = Drawing.ContentAlignment.MiddleLeft .Padding = New Windows.Forms.Padding(20, 0, 0, 0) End With

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)
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)
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()
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()
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
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
- Added percentage format property, allowing you to use custom formats
- Fixed XP bug, that caused the percentage not to be displayed properly
- Created SourceForge project for SVN capabilities
Changes in version 1.0.9
- Fixed a number of dispose issues (a LOT of thanks to JohnH for helping me with this!)
- Fixed bug in percentage calculation when using multiple decimals
- Added an auto increment function to the demo and added decimals to a few bars
Changes in version 1.0.6
- Improved component efficiency by not recalculating some values for every draw event
- Added multiple new events
Changes in version 1.0.5
- Added extra documentation and events
Changes in version 1.0.4
- Fixed alignment bug
- Added padding property
|
|
 |
 | XP 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
|
|
|
|
 |
|
 |
hi, invalidate your control before you call ShowText to prevent the bug 
greetz
|
|
|
|
 |
|
 |
Hey,
I'll give that a try 
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!
|
|
|
|
 |
|
 |
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 
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!
|
|
|
|
 |
 | Another 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.
|
|
|
|
 |
|
 |
Hey,
The current version already includes a ForeColor property. Is this not working in your implementation? (If this is the case I'll have a closer look at it)
Cheers BN
|
|
|
|
 |
|
 |
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.
|
|
|
|
 |
|
 |
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' 
Btw, if you find anything, plz let me know and I'll try to implement if possible 
Cheers BN
|
|
|
|
 |
 | Thank 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.
|
|
|
|
 |
|
 |
I'm happy to contribute this little component to the open source world 
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!
|
|
|
|
 |
 | manifest 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.
|
|
|
|
 |
|
 |
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!
|
|
|
|
 |
|
 |
Visual Studio 2008 Pro Edition, Fully Updated
rspercy 1 + 1 = 186,440....Depending on the species.
|
|
|
|
 |
|
 |
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!
|
|
|
|
 |
|
 |
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.
|
|
|
|
 |
|
 |
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 
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!
|
|
|
|
 |
|
 |
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.
|
|
|
|
 |
|
 |
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.
|
|
|
|
 |
 | Graphics 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.
DaveBTW, 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)
|
|
|
|
 |
|
 |
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 
Cheers BN
|
|
|
|
 |
|
 |
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
DaveBTW, 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)
|
|
|
|
 |
|
 |
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!
using System; using System.ComponentModel; using System.Drawing; using System.Windows.Forms;
namespace WinForms.Controls { [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;
[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;
public ProgressBarWithPercentage() { overlayColor = Color.White; stringFormat = new StringFormat(); percentageVisible = true; stringFormat.Alignment = StringAlignment.Center; stringFormat.LineAlignment = StringAlignment.Center; }
[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; } }
[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;
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()) { 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); } 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(); } } }
DaveBTW, 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
|
|
|
|
 |
|
|
 |
|
|