Riples
Riples

Reputation: 1157

Trying To Update GUI Within Thread (Started From BackgroundWorker)

I am having issues working with GUI updates on different threads. My scenario is this:

I have a main form with a single checkbox on it. In the Form_Load event I start a background worker. In the BackgroundWorker_DoWork event I call a new class which in turn starts a new thread. From this thread I am trying to set the Checked state of the checkbox on Form1 but without any luck.

So far I have the following code sample:

Public Class Form1
    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        Try
            BackgroundWorker1.RunWorkerAsync()

        Catch ex As Exception
            MsgBox(ex.ToString)
        End Try
    End Sub

    Private Sub BackgroundWorker1_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
        Try

            Do While Not BackgroundWorker1.CancellationPending = True
                Dim cl As New HandleClient
                Me.Invoke(Sub() Checkbox1.Checked = True)
            Loop

        Catch ex As Exception
            MsgBox(ex.ToString)
        End Try
    End Sub
End Class

Public Class HandleClient

    Public Sub startClient()
        Dim ctThread As Threading.Thread = New Threading.Thread(AddressOf start)
        ctThread.Start()
    End Sub

    Private Sub start()
        While (True)
            Try
                ...
                ..

            Catch ex As Exception
                    Form1.Invoke(Sub() Form1.Checkbox1.Checked = False) '<== Fails here
            End Try
        End While
    End Sub
End Class

I have tried using ThreadSafe calls and setting properties through a separate class but I either get an error stating that I can't use BeginInvoke or the Checkbox just doesn't update (without error).

Any help appreciated thanks.

Upvotes: 1

Views: 388

Answers (1)

user4615122
user4615122

Reputation:

I'm not sure why you are starting a new thread from a background worker that has already started a new thread for the worker. Seems like an extra unneeded thread. The backgroundworker worker thread can't update controls that are owned by the UI thread which is the same reason why your new handle client thread can't update it either. With the background object you can update the UI thread by raising the ProgressChanged event of the background worker and place the updating code into the method that handles the event.

Public Class Form1
    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        Try
            BackgroundWorker1.WorkerReportsProgress = true
            BackgroundWorker1.RunWorkerAsync()

        Catch ex As Exception
            MsgBox(ex.ToString)
        End Try
    End Sub

    Private Sub BackgroundWorker1_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork

        Dim worker As BackgroundWorker = CType(sender, BackgroundWorker)

        Try

            Do While Not worker.CancellationPending = True
                ' Do the thing that is in the Try block of the HandleClient
                Try
                    ' ...
                    ' ..
                    ' Using a value of 1 for true, since you used False in the catch statement
                    worker.ReportProgress(1)
                    ' Or you can use the overloaded method
                    worker.ReportProgress(0, True)
                Catch ex As Exception
                    ' Form1.Invoke(Sub() Form1.Checkbox1.Checked = False) '<== Fails here
                    ' Using a value of 0 for false
                    worker.ReportProgress(0)
                    ' Or you can use the overloaded method
                    worker.ReportProgress(0, False)
                End Try

            Loop

        Catch ex As Exception
            MsgBox(ex.ToString)
        End Try
    End Sub

    Private Sub backgroundWorker1_ProgressChanged(ByVal sender As Object, ByVal e As ProgressChangedEventArgs) Handles backgroundWorker1.ProgressChanged
        ' If you used the overloaded method, you can delete this case statement
        Select Case e.ProgressPercentage
            Case 0
                Form1.Checkbox1.Checked = True
            Case 1
                Form1.Checkbox1.Checked = False
            Case Else 
                Form1.Checkbox1.CheckState = CheckState.Indeterminate
        End Select

        ' Or just use the userstate as mention in the comments
        Form1.Checkbox1.Checked = DirectCast(e.UserSate, Boolean)

    End Sub 

End Class

Upvotes: 4

Related Questions