Click here to Skip to main content
15,894,410 members
Articles / Programming Languages / Visual Basic
Article

A Never-ending ProgressBar

Rate me:
Please Sign up or sign in to vote.
4.36/5 (37 votes)
30 Sep 2004CPOL2 min read 229.4K   1.3K   100   68
A progress bar for when you don't know how long a process will take.

Sample screenshot

Introduction

Sometimes you just don’t know how long something will take. Like when your wife says she’ll only be a few minutes in the grocery store…you know what I mean.

The standard ProgressBar that ships with Visual Studio is great for when you know how long something will take or there is a way to determine just how many steps a process will have. But like the trip to the store with your wife, sometimes you just can’t know how long something’s going to take. That’s why I created this progress bar.

The code

The control is very simple in that all it does is use the standard Graphics methods to draw into a Rectangle object.

VB
Private Sub OSProgressBar_Paint(ByVal sender As Object, _
         ByVal e As System.Windows.Forms.PaintEventArgs) _
         Handles MyBase.Paint
    Me._Graphics = e.Graphics
    Me._Graphics.SmoothingMode = Drawing2D.SmoothingMode.HighSpeed

    'this stops some of the redraw flickering at higher speeds
    If _RequireClear Then
        Me._Graphics.Clear(Me.BackColor)
    End If

    DrawBackGround()
End Sub

Private Sub PositionIndicator(ByVal Rect As Rectangle)
    If Not IsNothing(Me._PointImage) AndAlso Me._ProgressType = _
                        OSProgressTypeConstants.osGRAPHICTYPE Then
        Me._Graphics.DrawImage(Me._PointImage, Rect)
    Else
        Select Case Me._ProgressBoxStyle
            Case OSProgressBoxStyleConstants.osSOLIDSAMESIZE
                Dim R2 As New Rectangle(Rect.Left + 3, _
                  Rect.Top + 3, Rect.Width - 5, Rect.Height - 5)
                Me._Graphics.FillRectangle(New SolidBrush(_IndicatorColor), R2)
            Case OSProgressBoxStyleConstants.osBOXAROUND
                Me._Graphics.DrawRectangle(New Pen(_IndicatorColor), Rect)
                Dim R2 As New Rectangle(Rect.Left + 3, Rect.Top + 3, _
                                        Rect.Width - 5, Rect.Height - 5)
                Me._Graphics.FillRectangle(New SolidBrush(_IndicatorColor), R2)
            Case OSProgressBoxStyleConstants.osSOLIDBIGGER
                Me._Graphics.FillRectangle(New SolidBrush(_IndicatorColor), Rect)
            Case OSProgressBoxStyleConstants.osSOLIDSMALLER
                Dim R2 As New Rectangle(Rect.Left + 5, Rect.Top + 5, _
                                        Rect.Width - 9, Rect.Height - 9)
                Me._Graphics.FillRectangle(New SolidBrush(_IndicatorColor), R2)
        End Select
    End If
End Sub

Private Sub DrawBackGround()
    Me._NumPoints = 0
    If Me.Width > 0 And Me.Height > 0 Then
        If Me._ShowBorder Then
            Me._Graphics.DrawRectangle(New Pen(SystemColors.ActiveBorder), _
                    New Rectangle(0, 0, Me.Width - 1, Me.Height - 1))
        End If
        Dim iBoxSize As Integer = Me.Height * 0.75
        Dim iBoxLeft As Integer = iBoxSize / 2
        If iBoxSize > 3 Then
            Do
                'entire area Rectangle for the background image
                Dim r As New Rectangle(iBoxLeft, 0, Me.Height - 1, Me.Height - 1)
                If r.Left + r.Width > Me.Width Then
                    Exit Do
                End If
                If Me._NumPoints = Me._Position Then
                    'draw position indicator in full rectagle
                    PositionIndicator(r)
                Else
                    'this will be the rectangle where 
                    'the background image is drawn
                    Dim R2 As New Rectangle(r.Left + 3, r.Top + 3, _
                                            r.Width - 6, r.Height - 6)
                    If Not IsNothing(Me._NormalImage) AndAlso Me._ProgressType = _
                                  OSProgressTypeConstants.osGRAPHICTYPE Then
                        Me._Graphics.DrawImage(Me._NormalImage, R2)
                    Else
                        Me._Graphics.FillRectangle(New SolidBrush(Me.ForeColor), R2)
                    End If
                End If
                iBoxLeft += (iBoxSize * 1.5)
                Me._NumPoints += 1
            Loop
        End If
    End If
End Sub

Private Sub OSProgressBar_Resize(ByVal sender As Object, _
            ByVal e As System.EventArgs) Handles MyBase.Resize
    Me._RequireClear = True
    'invalidate the control
    Me.Invalidate()
End Sub

Private Sub tmrAutoProgress_Tick(ByVal sender As Object, _
       ByVal e As System.EventArgs) Handles tmrAutoProgress.Tick
    If Me._Position = Me._NumPoints - 1 Then
        If Me._ProgressStyle = OSProgressStyleConstants.osLEFTTORIGHT Then
            Me._Position = 0
        Else
            Me._Position -= 1
            Me._Increasing = False
        End If
    ElseIf Me._Position = 0 And Not Me._Increasing Then
        Me._Position += 1
        Me._Increasing = True
    Else
        If Me._Increasing Then
            Me._Position += 1
        Else
            Me._Position -= 1
        End If
    End If
    Me._RequireClear = False
    Me.Invalidate()
End Sub

The user can choose whether to have the standard boxes drawn in one of four types (same size as background box, smaller, larger, or smaller with a box around), or the user can select to use images (icons work best) for the background and position indicators.

Rectangle size and number of positions are determined by the height of the control when placed on a form. For instance, if the control is tall, there will be fewer rectangles, but they will be larger. Alternately, if it is short, more rectangles and smaller. Currently, the control will only operate properly when horizontal (width is greater than the height), but I don't think the code would be too difficult to modify for vertical operation.

For a box type progress bar, developers can set the BackGroundColor, the ForeGroundColor (color of the background boxes), the IndicatorColor (color of the position boxes). For a graphic type progress bar, NormalImage (background image) and PointImage (position image) images can be set.

Developers can determine if the control "autoprogresses", that is, if it changes position automatically until the developer stops it. If AutoProgress is enabled, two more properties come into play; AutoProgressSpeed and ProgressStyle. The speed is self explanatory, 1 is slow, 255 is fast. The ProgressStyle property determines that when the indicator reaches the last point, if it starts over at the first point, or turns around and heads back in the other direction. Of course, the developer can also set the position manually if so desired.

Just a note concerning display of progress type controls. If you have a long process that you don't control, the current thread may block during the process. I've found this to be so when serializing and deserializing classes. In this instance, any method you use that is running within the same thread will also be blocked. I've tried animated GIFs for this purpose, and they also stop animating when blocking occurs. It's necessary to spawn a new thread prior to calling the blocking function and then display the necessary progress indicator.

License

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


Written By
United States United States
Visual Basic Developer since version 1.0
Java web developer
Currently developing in vb and c#

Comments and Discussions

 
AnswerRe: How to use it. Pin
Greg Osborne31-May-07 5:29
Greg Osborne31-May-07 5:29 
GeneralRe: How to use it. Pin
Mor DeRor21-Jun-07 13:43
Mor DeRor21-Jun-07 13:43 
GeneralProgress bar: Determining Programme Execution Time Pin
tj289-May-06 2:54
tj289-May-06 2:54 
GeneralRe: Progress bar: Determining Programme Execution Time Pin
Greg Osborne9-May-06 3:53
Greg Osborne9-May-06 3:53 
QuestionNice Control Pin
Dwain Snickles22-Mar-06 6:08
Dwain Snickles22-Mar-06 6:08 
GeneralI don't get it. Pin
possumfoot6-Jan-06 11:32
possumfoot6-Jan-06 11:32 
GeneralRe: I don't get it. Pin
Patrick Etc.7-Jan-06 11:59
Patrick Etc.7-Jan-06 11:59 
GeneralThis is PERFECT! Pin
Patrick Etc.5-Jan-06 18:42
Patrick Etc.5-Jan-06 18:42 
Greg, I followed your instructions very closely and WOW this is amazing! It really puts a professional touch on the application I'm creating - thank you so much!

So that others can do this as easily as I did:

Download the source; you don't need to recompile, just put the DLL file included in a convenient place, go to your project References, select Add Reference, and Browse to where you put the DLL.

Now, open the Toolbox, right-click and select "Add/Remove Items", again, browse to the DLL, and select OK.

Now the OSProgressBar shows up under "User Controls."

Create a new form; I called mine NEProgressBar
(Never Ending Progress Bar Big Grin | :-D )

Drag the progress bar onto it. You now have a basic progress bar with square progress indicators.

Create a new class; I called mine LoaderThread.

Create a new Sub New, if you need to pass in additional arguments to the thread - an object you're trying to populate, for example.

Copy in the code that Greg has provided in two other threads in the comments, and customize the Start subroutine to your needs. For mine, it involved contacting a webserver and downloading information, so my Start routine did that stuff. The basic format of my start routine looks like this:

    <br />
Private Sub Start()<br />
        Try<br />
            'Do your running here<br />
            Dim wC as new WebClient()<br />
            wC.DownloadData("http://www.yahoo.com")<br />
        Catch ex As Exception<br />
            _ErrorString = "Load failed:" + vbCrLf + vbCrLf _<br />
                            + "URL: " + "http://www.yahoo.com" + vbCrLf _<br />
                            + "Error: " + ex.Message<br />
        Finally<br />
            _LoadComplete = True<br />
        End Try<br />
    End Sub<br />


Now, go back to the form you created; in this example, NEProgressBar

In the NEProgressBar_Load subroutine (which fires when the form is done loading), which you can obtain by double-clicking the form in the Form Designer, you can simply use the following code, customized to your needs:

<br />
        Dim DocLoader As New WebLoaderThread([optional args here])<br />
        DocLoader.Spinup()<br />
        Do Until DocLoader.LoadComplete<br />
            Application.DoEvents()<br />
        Loop<br />
        DocLoader.SpinDown()<br />
<br />
        If DocLoader.LoadComplete Then<br />
            'Do your SUCCESS processing here<br />
        Else<br />
            'Do your FAILURE processing here.<br />
            MsgBox(DocLoader.ErrorValue)<br />
        End If<br />


Finally, in your main application, simply do:

<br />
Dim myForm as New NEProgressBar<br />
myForm.Show() ' causes the Load event to fire when done<br />
' Do anything with the output of the NEProgressBar form<br />
' For example, you can give the form some Properties which it can<br />
' pass into the Thread, where I put [optional args here], <br />
' and then when the progess is done, simply do:<br />
Dim myVal as String = myForm.GetSomeProperty()<br />
<br />
' Finally, close the progress window<br />
myForm.Close()<br />


That's all!

Should be that simple.

Hope this helps someone.
Questionerror with dll Pin
thandal27-Apr-06 5:11
thandal27-Apr-06 5:11 
GeneralRe: This is PERFECT! Pin
MMRR27-Jul-06 9:04
MMRR27-Jul-06 9:04 
Questionhow to run the application Pin
royalbmo8-Dec-05 7:27
royalbmo8-Dec-05 7:27 
AnswerRe: how to run the application Pin
Greg Osborne8-Dec-05 7:48
Greg Osborne8-Dec-05 7:48 
QuestionHow do you use the ProgressBar Pin
Bob Sarrett19-Nov-05 8:26
Bob Sarrett19-Nov-05 8:26 
GeneralProperties reset Pin
boldtbanan21-Sep-05 10:47
boldtbanan21-Sep-05 10:47 
QuestionHow To Use Progress Bar Pin
Sandeep Nagra25-Apr-05 14:04
Sandeep Nagra25-Apr-05 14:04 
QuestionJust works in debug mode? Pin
BasGo21-Mar-05 2:46
BasGo21-Mar-05 2:46 
GeneralNew Bie Pin
AgnesCheng12-Mar-05 0:00
AgnesCheng12-Mar-05 0:00 
GeneralRe: New Bie Pin
GregOsborne14-Mar-05 3:58
GregOsborne14-Mar-05 3:58 
QuestionThreading? Pin
Olaf.Rabbachin5-Nov-04 5:17
Olaf.Rabbachin5-Nov-04 5:17 
AnswerRe: Threading? Pin
GregOsborne5-Nov-04 6:05
GregOsborne5-Nov-04 6:05 
GeneralRe: Threading? Pin
Olaf.Rabbachin5-Nov-04 7:49
Olaf.Rabbachin5-Nov-04 7:49 
GeneralRe: Threading? Pin
GregOsborne5-Nov-04 8:15
GregOsborne5-Nov-04 8:15 
GeneralRe: Threading? Pin
Ramesh11046613-May-05 10:47
Ramesh11046613-May-05 10:47 
GeneralFlicker Pin
Brett Venson23-Oct-04 2:42
Brett Venson23-Oct-04 2:42 
GeneralRe: Flicker Pin
Judah Gabriel Himango18-Mar-05 7:19
sponsorJudah Gabriel Himango18-Mar-05 7:19 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.