Multithreading for a progressbar and code locations (vb.net)?

KekuSemau picture KekuSemau · Sep 21, 2012 · Viewed 10.4k times · Source

I am stuck updating a progressbar from a different thread. I did get it running in the simplest way, but then cleaning the code gets me stuck.

My testing code looks like all the examples on the web related to backgroundworker and BeginInvoke.

FormP is the Progressbar-Form. This works:

Public Class Form1
Private Delegate Sub delegate_ProgressUpdate(ByVal paramValue As Integer,
  ByVal paramMax As Integer)

Private Sub Button1_Click(sender As System.Object,
  e As System.EventArgs) Handles Button1.Click
    ' Test 01:
    ' Show Progressbar via BGW
    ' All functions are within Form1
    Dim bgw As New BackgroundWorker()
    AddHandler bgw.DoWork, AddressOf BGW_Sample01_DoWork

    FormP.Show(Me)
    bgw.RunWorkerAsync()
End Sub

Private Sub invokeMe_ProgressUpdate(ByVal paramValue As Integer, ByVal paramMax As Integer)
    FormP.ProgressBar1.Maximum = paramMax
    FormP.ProgressBar1.Value = paramValue
    FormP.ProgressBar1.Update()
End Sub

Private Sub BGW_Sample01_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs)
    For i As Integer = 1 To 10
        Threading.Thread.Sleep(500) ' Test delay
        Me.BeginInvoke(New delegate_ProgressUpdate(AddressOf invokeMe_ProgressUpdate),
                       i, 10)
    Next
    MessageBox.Show("Fertig")
End Sub

If I try to make things work more orderly encapsulated in FormP, it doesn't work.

Private Sub Button2_Click(sender As System.Object, e As System.EventArgs) Handles Button2.Click
    Dim bgw As New BackgroundWorker
    AddHandler bgw.DoWork, AddressOf BGW_Sample02_DoWork
    FormP.Show(Me)
    bgw.RunWorkerAsync()
End Sub

Private Sub BGW_Sample02_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs)
    For i As Integer = 1 To 10
        Threading.Thread.Sleep(500)
        FormP.SetProgress(i, 10)
    Next
    MessageBox.Show("Fertig")
End Sub


' ########## FormP #################

Public Class FormP

Private Delegate Sub delegate_ProgressUpdate(ByVal value As Integer, ByVal maximum As Integer)

Public Sub SetProgress(ByVal paramValue As Integer, ByVal paramMaximum As Integer)
    If Me.InvokeRequired Then
        Me.Invoke(New delegate_ProgressUpdate(AddressOf Me.SetProgress), paramValue, paramMaximum)
    Else
        Me.ProgressBar1.Maximum = paramMaximum
        Me.ProgressBar1.Value = paramValue
        Me.ProgressBar1.Update()
    End If
End Sub

End Class

FormP does not freeze, but UI is not updated. Actually Me.InvokeRequired is false and I think that's where I begin to miss some important parts. I tried Form1.InvokeRequired here, but it's false as well. My understanding is: the calling thread here is the bgw thread, no matter in what class the code is that this thread calls... That seems not to be it?

Thanks for any thoughts.

Answer

KekuSemau picture KekuSemau · Oct 27, 2012

What worked eventually:

Private frmP As FormP

Private Sub Button2_Click(sender As System.Object, e As System.EventArgs) Handles Button2.Click
    Dim bgw As New BackgroundWorker
    If Me.frmP IsNot Nothing AndAlso Me.frmP.Visible Then Return
    Me.frmP = New FormP
    Me.frmP.Show(Me)
    AddHandler bgw.DoWork, AddressOf BGW_Sample02_DoWork
    bgw.RunWorkerAsync(New Object() {Me.frmP})
End Sub

Private Sub BGW_Sample02_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs)
    Dim objFrmP As FormP = DirectCast(e.Argument(0), FormP)
    For i As Integer = 1 To 10
        objFrmP.setProgress(i, 10)
        Threading.Thread.Sleep(500)
    Next
    MessageBox.Show("Finished")
End Sub

The Progress-Dialog-Code in FormP:

Public Sub setProgress(paramValue As Integer, paramMaximum As Integer)
    If Me.InvokeRequired Then
        ' defining a delegate type is not really necessary
        Me.Invoke(Sub() Me.setProgress(paramValue, paramMaximum))
    Else
        Me.ProgressBar1.Maximum = paramMaximum
        Me.ProgressBar1.Value = paramValue
        Me.ProgressBar1.Update()
    End If
End Sub