尝试在线程内更新GUI(从BackgroundWorker启动)

时间:2015-03-12 12:37:07

标签: vb.net multithreading

我在使用不同线程的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只是没有更新(没有错误)。

任何帮助表示感谢。

1 个答案:

答案 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