Dev Tools: simplest Progress Bar

Collapse
X
 
  • Time
  • Show
Clear All
new posts
  • jimatqsi
    Moderator Top Contributor
    • Oct 2006
    • 1293

    Dev Tools: simplest Progress Bar

    Here is my take on the progress bar. In my view it's the simplest progress bar possible, and provides the greatest control to the developer. You can choose to base your progress bar on a text box, label, rectangle or line. You get to choose the colors, size and other attributes of the progress bar object. No Active/X object or class definition necessary.

    To use the progress bar just add one (textbox or label) or two (rectangle or line) objects to your form. Decide on the position, dimensions and colors as you like. I prefer to make them invisible until used. Then call the wsProgressBar subroutine, providing the control(s) to be used and the variables necessary to compute progress percentage.

    The sample attached to this article has 1 form showing 9 variations on the progress bar; 10 if you consider the two slanted lines together as representing one progress bar object. It was a kind of neat surprise to see that.

    Here's the code. In the sample it's within the code-behind of the form; normally it would be in some public module so it would be callable from any form. I've included the kitchen sink; you might want to strip out some options.

    Code:
    ' Procedure : ProgressBar
    ' Author    : Jim Wolf, Wolf Software, LLC
    ' Date      : 5/30/2014
    ' Purpose   : Simple progress bar made up of on-screen controls (label, textbox, line or rectangle)
    '             1 Rectangle/line will have a length or height that represents progress so far
    '             2nd rectangle/line's max length or height gives something to measure progress against
    ' progress - how many units have been completed
    ' goal - # of units that is 100% complete
    ' bDoEvents - yes/no flag to indicate whether this routine should DoEvents before returning
    ' pbPctCtl - control to show the percent complete on. Use NULL if no % display wanted
    ' pbCTL1 = the control that represents 100%
    ' pbCTL2 = the control that grows to show percent complete
    '
    '---------------------------------------------------------------------------------------
    '
    Public Sub wsProgressBar(progress As Long, goal As Long, bDoEvents As Boolean, pbPctCtl, pbCTL1 As Control, Optional pbCTL2 As Control)
    
       On Error GoTo ProgressBar_Error
    
        Dim dblPCT As Double
        Dim pbOrientation As Byte   ' = 1 for making the control wider as progress increases
                                    ' not 1 for making the control taller as progress increases
        Dim pbCTL As Control        ' The control that will vary based on progress
        
        If goal = 0 Then Exit Sub
        If goal > 0 And progress < 0 Then Exit Sub ' if either is negative both must be
        If goal < 0 And progress > 0 Then Exit Sub
        
        If pbCTL1.Height >= pbCTL1.Width Then
            pbOrientation = 0  ' vertical progress bar
        Else
            pbOrientation = 1  ' progress bar will be horizontal
        End If
        
        If pbCTL2 Is Nothing Then
            Set pbCTL = pbCTL1 ' if only one control is provided there will be no goal shown
        Else
            Set pbCTL = pbCTL2
        End If
        
        pbCTL1.Visible = True   ' show the goal
        If progress = 0 Then
            pbCTL.Visible = False
        Else
            pbCTL.Visible = True   ' show the progress control
        End If
        
        dblPCT = Abs(progress) / Abs(goal) ' percentage complete
        If dblPCT > 1 Then dblPCT = 1
        
        pbCTL.Left = pbCTL1.Left
                
        Select Case pbCTL1.ControlType
            Case acRectangle
                    pbCTL.Top = pbCTL1.Top
                    If pbOrientation = 1 Then  ' horizontal?
                        pbCTL.Width = pbCTL1.Width * dblPCT  ' more progress =wider control
                    Else
                        pbCTL.Height = pbCTL1.Height * dblPCT ' more progess=taller control
                        pbCTL.Top = pbCTL1.Top + (pbCTL1.Height - pbCTL.Height) ' but start at bottom of the goal
                    End If
            Case acLine
                    If pbCTL.Name = pbCTL1.Name Then
                    Else
                        If pbCTL1.Height = 0 Then    ' is this a horizontal line
                            pbCTL.Top = pbCTL1.Top
                            pbCTL.Width = pbCTL1.Width * dblPCT  ' more progress =wider control
                        
                        ElseIf pbCTL1.Width = 0 Then ' is this a vertical line
                            pbCTL.Left = pbCTL1.Left
                            pbCTL.Height = pbCTL1.Height * dblPCT ' more progess=taller control
                            pbCTL.Top = pbCTL1.Top + (pbCTL1.Height - pbCTL.Height) ' but start at bottom of the goal
                        Else                        ' it must be a slanted line
                            pbCTL.Height = pbCTL1.Height * dblPCT ' more progess=taller control
                            pbCTL.Top = pbCTL1.Top
                        End If
                    End If
            Case acTextBox, acLabel
                If pbCTL.Vertical Then    ' is this textbox showing text vertically?
                    pbCTL.TopMargin = (1 - dblPCT) * pbCTL.Height  ' make margin as big as the part not yet done
                Else
                    pbCTL.RightMargin = (1 - dblPCT) * pbCTL.Width ' make margin as big as the part not yet done
                End If
        End Select
        
        If IsNull(pbPctCtl) Then
        Else
            Select Case pbPctCtl.ControlType  ' can only show % on textboxes, labels and buttons
                Case acTextBox
                    pbPctCtl.Value = Format(dblPCT, "#.#%")  ' display the percentage complete
                Case acLabel, acCommandButton
                    pbPctCtl.Caption = Format(dblPCT, "#.#%")
                
            End Select
        End If
        
        Set pbCTL = Nothing
        If bDoEvents Then DoEvents
        On Error GoTo 0
       Exit Sub
    
    ProgressBar_Error:
    
        MsgBox "Error " & Err.Number & " " & Err.Description
         Resume Next
    
    End Sub
    Attached Files
  • twinnyfo
    Recognized Expert Moderator Specialist
    • Nov 2011
    • 3662

    #2
    jimatqsi,

    I have been using a Progress Bar which operates on much simpler principles:

    On your Form, you have three objects:
    1. A Rectangle (rctProgressBar ), with your choice of colors.
    2. A Label (lblWorkingBott om), which is placed on top of the Rectangle.
    3. Another Label (lblWorkingTop) , whihc is placed on top of the other two controls. These are not Vivisble when the Form opens.

    All three of these Objects will have a maximum width, set as a Private Constant (expressed in twips) when the Form opens:

    Code:
    Option Explicit
    Option Compare Database
    
    Private Const ProgressMax = 3600
    Then, you need three brief Procedures in the VBA used to manipulate these objects:

    Code:
    Private Sub ShowProgressBar()
        Me.rctProgressBar.Visible = True
        Me.lblWorkingTop.Visible = True
        Me.lblWorkingBottom.Visible = True
    End Sub
    Private Sub HideProgressBar()
        Me.rctProgressBar.Visible = False
        Me.lblWorkingTop.Visible = False
        Me.lblWorkingBottom.Visible = False
    End Sub
    Private Sub UpdateProgressBar(Max As Integer, Current As Integer)
        Dim x
        Me.rctProgressBar.Width = ProgressMax * (Current / Max)
        Me.lblWorkingTop.Width = ProgressMax * (Current / Max)
        Me.lblWorkingBottom.Caption = "           Working...." & Format(Current / Max, "0%")
        Me.lblWorkingTop.Caption = "           Working...." & Format(Current / Max, "0%")
        x = DoEvents
    End Sub
    Finally, when you are performing repeating actions in the Form, you must call the functions appropriately:

    Code:
    Private Sub PerformActions()
    On Error GoTo EH
        Dim intCount As Integer
        Dim intProgress As Integer
    
        ShowProgressBar
    
        intCount = DCount("*", "tblMyData")
        For intProgress = 1 to intCount
            'Perform Your actions here
            UpdateProgressBar intCount, intProgress
        Next intProgress
    
        HideProgressBar
        Exit Sub
    EH:
        MsgBox "There was an error performing these actions!  " & _
            "Please contact your Database Administrator.", vbCritical, "Error!"
        Exit Sub
    End Sub
    This method is incredibly simple and highly customizable. It also manages all your Progress Bar needs in the space of only a few lines of Code, which can be added to almost any process, as long as you know the total number of processes, and the current process you are on.

    I may be way off mark, but this code has worked flawlessly for me for more than two years!

    Comment

    • jimatqsi
      Moderator Top Contributor
      • Oct 2006
      • 1293

      #3
      twinnyfo,
      We are using essentially the same methodology. What I intended to demonstrate was that one can make a progress bar without resorting to Active/X objects or even class definitions. We just need references to a few objects (as few as 1 for my implementation, as many as 3 for yours and mine) to make it work.

      The pertinent thing from my point of view is that the developer has complete control over the progress bar's position and appearance. It doesn't have to be a pop-up, it can be embedded within the active form. Controls can even do double-duty as part-time progress bar elements, such as the button I used to launch my test and also show the progress %.

      Admittedly, I threw everything including the kitchen sink into my progress bar. Most folks are going to use only one type. Who needs to be able to use rectangles and lines and labels and text boxes for that purpose? Probably nobody, but it's nice to have those options available. I've standardized on using two lines, bright red on black, but sometimes I like to put some interesting text into an expanding label.

      I like your solution because of the points I made in the first paragraph. Two points, however. I prefer keeping the show, hide and updating all in one subroutine. And I pass the controls to a public subroutine so the logic can be made available to all forms rather than be made a part of each form. For my example, however, I embedded the code in the form.

      Thanks for your comments.

      Jim

      Comment

      Working...