Reputation: 1157
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
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