我在使用不同线程的GUI更新时遇到问题。我的情况是:
我有一个主窗体,上面有一个复选框。在Form_Load
事件中,我启动了一个后台工作者。在BackgroundWorker_DoWork
事件中,我调用一个新类,该类又开始一个新线程。从这个线程我试图在Form1上设置复选框的Checked
状态,但没有任何运气。
到目前为止,我有以下代码示例:
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
我尝试过使用ThreadSafe调用并通过单独的类设置属性但是我得到一个错误,指出我不能使用BeginInvoke或者Checkbox只是没有更新(没有错误)。
任何帮助表示感谢。
答案 0 :(得分:4)
我不确定你为什么要从已经为worker创建一个新线程的后台worker开始一个新线程。看起来像一个额外不需要的线程。 backgroundworker工作线程无法更新UI线程拥有的控件,这与您的新句柄客户端线程无法更新它的原因相同。使用后台对象,您可以通过引发后台工作程序的ProgressChanged
事件来更新UI线程,并将更新代码放入处理事件的方法中。
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