Introduction
Progress bar controls make up an awful lot of the Windows UI; heck, what would we do without them? They are there wherever we need to be told how far our computer is through a task. They are wonderfully simple and easy to use in the .NET framework, and hence, easy to make with a couple-o-panels and a UserControl. A few quick properties and functions, and you’ve got your very own progress bar control to use in your WinForms applications.
However, somewhere deep within a building at Microsoft, a programmer created the Windows progress bar control, which is, in my opinion, rather unfriendly. You can’t even change the colour of the bar without an enormous effort, if at all! Well… we’ll soon change all that.
Background
Our end result will be a marvel of simplicity and a triumph of design, something like this:
The bar colour, unfilled bar colour, border colour, border visibility, and gloss visibility will all be fully customizable, bringing a little extra spice to your applications.
Now, we won't be writing oodles of complex code in order to achieve this. Don't tell my IT teacher, but we'll be using... panels. Yes! Panels! The humble panel will be put to good use in our project. In fact, the entire control is based around them, four of them to be precise. But, before any of you out there begin to ask why I didn't just use GDI+, it's because panels will do just fine. Why overcomplicate things?
Using the code
First things first, we'll create a new VB Express 2008 Class Library project, delete 'Class1', and add a new UserControl called “ColourProgressBar
”, and resize the control in the designer to about 200 pixels by thirty. You can already see it taking shape! That was fast!
Add a Panel
, name it ProgressFull
, and dock it to the left; then another, dock it to 'Fill
', and name it ProgressEmpty
. Change the BackColor
property of ProgressFull
to whatever you like, apart from 'Control
' (this is just so that we can see the results of our work). You'll notice that it is beginning to look more and more like a progress bar, but we still need to add the gloss. Put a Panel
inside the right-hand Panel
. Dock it at the top, resize its height to 10, and call it GlossRight
; do the same on the left and call it GlossLeft
. Then, we just change the BackColor
to 100, 255, 255, 255. This sets the BackColor
of the gloss panels to be semi-transparent white, adding to the overall aesthetics of the control.
Finally, set the padding of the actual control holding all of our panels to 1,1,1,1. This creates a thin border around our progress bar, which means, all we’ve got to do in order to change the colour of the border is to change the background colour.
Go ahead and click 'debug'. Change the BackColor
of the control in the TestContainer
, and see how the border colour changes. Pretty cool, huh?
Alright! Now, our control looks just like the final result:
Now, we've just got to make it work, which is really not that hard. First, we've got to import a couple of things to aid writing our code, which really is as simple as adding these lines to the top of our code:
Imports System.ComponentModel
Imports System.Drawing
These lines just import all the necessary pieces of the .NET framework that we'll need to code our progress bar. Next, a few global variables to carry data between our functions:
Private ProgressUnit As Integer = 20
- The amount, in pixels, that our progress bar increments in.Private ProgressValue As Integer = 1
- The amount of the MaxValue
that is filled.Private MaximumValue As Integer = 10
- The maximum value of the progress bar.Private IsBorderEnabled As Boolean = True
- Sets whether the border is enabled on the progressbar.Private ShowGloss As Boolean = True
- Sets whether the gloss is shown on the progressbar.
So, with that out of the way, let’s outline our main functions. These are simple, and there are only two of them.
UpdateProgress()
– An all-in-one function that updates the progress bar, changing it according to the global variables.UpadateGloss()
- A separate function to change the gloss according to the global variables.
By far, the UpdateGloss()
function is the simplest. It only carries out a very simple operation, resizing each GlossPanel
according to the size of the UserControl:
Private Function UpdateGloss() As Boolean
Try
GlossPanelLeft.Height = (Me.Height / 3)
GlossPanelRight.Height = GlossPanelLeft.Height
Catch MyException As Exception
Return False
Exit Function
End Try
Return True
End Function
The UpdateProgress()
function is longer, but remains similar in principle.
Private Function UpdateProgress() As Boolean
Try
ProgressUnit = (Me.Width / MaximumValue)
ProgressFull.Width = (ProgressValue * ProgressUnit)
If ShowGloss Then
GlossPanelLeft.Visible = True
GlossPanelRight.Visible = True
Else
GlossPanelLeft.Visible = False
GlossPanelRight.Visible = False
End If
If ProgressValue = MaximumValue Then
If IsBorderEnabled Then
ProgressFull.Width = (Me.Width - 2)
Else
ProgressFull.Width = Me.Width
End If
End If
If IsBorderEnabled Then
Me.Padding = New Windows.Forms.Padding(1)
Else
Me.Padding = New Windows.Forms.Padding(0)
End If
Catch MyException As Exception
Return False
Exit Function
End Try
Return True
End Function
As you can clearly see from the code, nearly everything revolves around the global variables. Will this create an unfriendly programming interface? You bet it won't. Before the days of .NET and other object-oriented programming methods, programming was very linear: user input was received, processed, and outputted, as simple as that. But now, with .NET, we can create so much more. Enter one of the greatest programming triumphs of all times! Properties!
Our control receives instructions for how to perform in the Forms Designer and in the program itself through properties, and our UserControl uses many of them. A few to control the looks and aesthetics, and a few to make things move and work in the way a progress bar should:
ProgressMaximumValue As Integer
– The maximum value of the progress bar.BorderColor As Color
– The colour of the progress bar border.ShowBorder As Boolean
– Whether the border is shown or not.IsGlossy As Boolean
– Whether the two GlossPanel
s are displayed or not.BarColor As Color
– The colour of the filled part of the progress bar.EmptyColor As Color
– The colour of the empty part of the progress bar.
I won't cover all of these properties in this article, as most of them are standard Get
-Set
style properties; however, a few of them are particularly important to note.
<DefaultValue(1), _
Description("The total progress of the ProgressBar."), _
Category("ProgressBar Properties")> _
Public Property Value() As Integer
Get
Return ProgressValue
End Get
Set(ByVal value As Integer)
If value <= MaximumValue Then
ProgressValue = value
Else
ProgressValue = MaximumValue
End If
UpdateProgress()
End Set
End Property
Firstly, notice that UpdateProgress()
is called at the end of the function. This is very important in that it is the UpdateProgress()
function that updates the progress bar according to the global variables. Without it, the properties would have no effect at all!
Next, see that we don't allow more than the maximum value of the progress bar to be entered in the progress bar property. In the event of too high a value being entered, the progress bar will default to the maximum value and will not go beyond the bounds of the control itself, where it can cause many inconveniences. However, in order to ensure robust programming, we must also place restrictions on the ProgressMaximumValue
property:
<DefaultValue(10), _
Description("The maximum value of the ProgressBar."), _
Category("ProgressBar Properties")> _
Public Property ProgressMaximumValue() As Integer
Get
Return MaximumValue
End Get
Set(ByVal value As Integer)
If value > Me.Width Then
MaximumValue = Me.Width
Else
MaximumValue = value
End If
UpdateProgress()
End Set
End Property
For a start, the MaximumValue
cannot be more than the width of the control in pixels. This can create serious problems, even lead to the bar not being displayed at all if MaximumValue
is set to something like 10000. And as standard, at the end, UpdateProgress()
is called.
Next, we add some event handlers to certain parts of our control that will affect one or more of our panels:
ColourProgressBar_Load
– Just call the UpdateProgress()
function to initialize the progressbar.ColourProgressBar_PaddingChanged
– Don’t allow padding to exceed the amount specified by the IsBorderEnabled
global.ColourProgressBar_Resized
– Adjust the size of the gloss according to the height and then call UpdateProgress()
.ColourProgressBar_DockChanged
– Pretty much the same reason as for the Resized
event, because controls resize when they dock.
Let’s have a quick look at the contents of two of these: the PaddingChanged
and Resize
events:
If IsBorderEnabled Then
Me.Padding = New Windows.Forms.Padding(1)
Else
Me.Padding = New Windows.Forms.Padding(0)
End If
This is the content of the PaddingChanged
event. All this does is ensure that the progress bar has correct padding depending on the IsBorderEnabled
variable. The Resized
event handler performs a similar task.
If Value <= MaximumValue Then
UpdateProgress()
End If
Calling the UpdateGloss
function realigns everything to reflect the size of the progress bar control. We're not done yet, however. We need to add some events to our control. An important one to add is an improved Click
event. This is important because only the actual control will respond to clicking at the moment, and if we click on any of our panels, the event is not raised. This is now complicated, however. We've just got to add a line of code:
Public Shadows Event Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs)
Piece of cake, see? In one swoop, we have created a reference to the Click
event. Now, we just add Click
event handlers to each of our four panels, with each event containing this line of code:
RaiseEvent Click(Me, e)
This just raises the Click
event for the actual control, as opposed to the panels inside it, for ease of coding when the control is included in applications. If something isn't covered here, take a look at the sample code and application. I like to think I've commented them pretty well.
And there you have it!
Quite possibly, the simplest approach to creating your own progress bars has just been unveiled for all to see, and what’s more, a UserControl that relies on panels has been shown to be just as effective as a GDI+ plain class library! So, there we have it, a control that will hopefully be useful as a starting point for controls of your own, or that can be used in your own applications when the Windows progress bar is too ugly, too unfriendly, or just downright unsuitable for the purpose you intend.
Credits
- A huge thank you to Mark James for his Silk Icon Pack (used in the demo app). Visit his site here.
History
- 19/12/08 - Article uploaded at 21:12.
- 22/12/08 - Article updated at 10:20 --
ProgressBarValue
changed to Value
to better replace the standard progress bar. The GlossOpacity
property added.