Search code examples
vb.netprogress-barbackgroundworker

Progress Bar and Background Worker


I have a progress bar and backgroundworker in VB.Net that I would want to work in a different form as given below:

Form1()
{
MaxRows = 10
for i = 0 to MaxRows then
// Update my value on the progressbar
....

next
}

ProgressBarForm

Private Sub ProgressBarForm_Shown(sender As Object, e As EventArgs) Handles Me.Shown
        TransferProgressBar.Visible = True
        ProgressBarBackgroundWorker.RunWorkerAsync()
    End Sub

    Private Sub ProgressBarBackgroundWorker_DoWork(sender As Object, e As ComponentModel.DoWorkEventArgs) Handles ProgressBarBackgroundWorker.DoWork
        For i = 0 To TransferProgressBar.Maximum
            'Dim Percentage As Integer = Math.Round(((i / (TransferProgressBar.Maximum - TransferProgressBar.Minimum)) * 100))
            ProgressBarBackgroundWorker.ReportProgress(i / 100)
        Next

    End Sub

    Private Sub ProgressBarBackgroundWorker_ProgressChanged(sender As Object, e As ComponentModel.ProgressChangedEventArgs) Handles ProgressBarBackgroundWorker.ProgressChanged
        TransferProgressBar.Value = e.ProgressPercentage
        PercentageLabel.Text = "Processing....." & TransferProgressBar.Value.ToString() & "%"
    End Sub

    Private Sub ProgressBarBackgroundWorker_RunWorkerCompleted(sender As Object, e As ComponentModel.RunWorkerCompletedEventArgs) Handles ProgressBarBackgroundWorker.RunWorkerCompleted
        MsgBox("Task Completed!")
        Me.Close()
    End Sub

How can I update my progressbar value using the backgroundworker from another form/Sub? Kindly let me know. I'm getting a bit confused here.


Solution

  • You seem to have it back to front. Rather than the BackgroundWorker handling the ProgressBar updates, use the BGW to do the time consuming work. Then at intervals you can provide updates for the ProgressBar/Form using the built-in ReportProgress method.

    Normally, you cannot (directly) access UI controls, like a ProgressBar, from a thread other than the one it was created on (ie the UI thread). The ReportProgress event is raised on the original/UI thread to make basic progress reporting easy.

    However, it is pretty much limited to only that. To do more, for instance post results of the long process to a ListBox, you would use a Delegate to update the control on the other/UI thread.

    The Form with the long running task

    Private WithEvents bgw As New BackgroundWorker
    Private frmProg As ProgForm          ' progress bar form
    
    ' start up
    Private Sub Button1_Click(sender ...etc
        ' set up 
        bgw.WorkerReportsProgress = True
        bgw.WorkerSupportsCancellation = True
    
        If frmProg Is Nothing Then          ' make sure progress form is instanced
            ProgForm = New frmProg
        End If
            
        If bgw.IsBusy = False Then
            frmProg.Show()
            bgw.RunWorkerAsync(10)         ' do some important work x10
        End If
    
    End Sub
    
    ' the job that will take a while
    Private Sub bgw_DoWork(sender As Object, e As DoWorkEventArgs) 
                      Handles bgw.DoWork
        ' ToDo: with multiple workers use sender, not 'bgw'
        ' get the amount of work to do
        Dim numToDo As Integer = CInt(e.Argument)
    
        ' important, time consuming work done here
        For n As Integer = 1 To numToDo
            ' do the "work"
            System.Threading.Thread.Sleep(100)
    
            ' post a notice, passing the percentage (raises the ProgressChanged event)
            bgw.ReportProgress(Convert.ToInt32((n / numToDo) * 100)  
        Next
    End Sub
    
    ' event raised from DoWork via ReportProgress
    Private Sub bgw_ProgressChanged(sender As Object, e As ProgressChangedEventArgs) 
          Handles bgw.ProgressChanged
        ' method added on the Progress form to 
    
        ' receive percentage and update the meter:
        frmProg.UpdateProgress(e.ProgressPercentage)
    
        ' if the progress bar was on the same form,
        ' update it directly:
        'MyProgBar.Value = MyProgBar.Maximum
        'MyProgBar.Value = pct
    End Sub
    
    ' optional event raised when the long running task is complete
    Private Sub bgw_RunWorkerCompleted(sender As Object, 
             e As RunWorkerCompletedEventArgs) Handles bgw.RunWorkerCompleted
    
        ' when all done, report that too
        MessageBox.Show("Work Complete!")
    End Sub
    

    Note that normally using Thread.Sleep freezes the UI. That doesnt happen here because it is the BackgroundWorker not the UI thread which is put to sleep.

    The Form with the progress bar:

    Public Sub UpdateProgress(pct As Integer)
    
       ' ToDo: Add error checking 
        progress.Value = progress.Maximum
        progress.Value = pct
    End Sub