Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / desktop / WinForms

A Better ProgressBar - Using Panels!

4.39/5 (24 votes)
29 Dec 2008CPOL8 min read 65.4K   2.9K  
Crafting your own progressbar UserControl from scratch.

Image 1

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:

Image 2

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!

Image 3

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:

Image 4

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:

VB
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:

VB
Private Function UpdateGloss() As Boolean

    Try

        'Hide Each GlossPanel [
        GlossPanelLeft.Height = (Me.Height / 3)
        GlossPanelRight.Height = GlossPanelLeft.Height
        ']

    Catch MyException As Exception

        'Return A Failure And Exit [
        Return False
        Exit Function
        ']

    End Try

    'Return A Success [
    Return True
    ']

End Function

The UpdateProgress() function is longer, but remains similar in principle.

VB
Private Function UpdateProgress() As Boolean

    Try

        'Recalculate Globals [
        ProgressUnit = (Me.Width / MaximumValue)
        ProgressFull.Width = (ProgressValue * ProgressUnit)
        ']

        'Hide Or Show GlossPanels According To Globals [
        If ShowGloss Then

            GlossPanelLeft.Visible = True
            GlossPanelRight.Visible = True

        Else

            GlossPanelLeft.Visible = False
            GlossPanelRight.Visible = False

        End If
        ']

        'If At Maximum Value, Fill The ProgressBar [
        If ProgressValue = MaximumValue Then

            If IsBorderEnabled Then

                ProgressFull.Width = (Me.Width - 2)

            Else

                ProgressFull.Width = Me.Width

            End If
        End If
        ']

        'Hide Or Show The Borders According To Globals [
        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 A Failure And Exit [
        Return False
        Exit Function
        ']

    End Try

    'Return A Success [
    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 GlossPanels 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.

VB
<DefaultValue(1), _
Description("The total progress of the ProgressBar."), _
Category("ProgressBar Properties")> _
Public Property Value() As Integer

    Get

        'Return The Property [
        Return ProgressValue
        ']

    End Get
    Set(ByVal value As Integer)

        'Don't Allow More Than The Maximum Value [
        If value <= MaximumValue Then

            ProgressValue = value

        Else

            ProgressValue = MaximumValue

        End If
        ']

        'Update The ProgressBar [
        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:

VB
<DefaultValue(10), _
Description("The maximum value of the ProgressBar."), _
Category("ProgressBar Properties")> _
Public Property ProgressMaximumValue() As Integer

    Get

        'Return The Property [
        Return MaximumValue
        ']

    End Get
    Set(ByVal value As Integer)

        If value > Me.Width Then

            MaximumValue = Me.Width
        Else

            MaximumValue = value
        End If

        '
        'Update The ProgressBar [
        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:

VB
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.

VB
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:

VB
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:

VB
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.

License

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