2015-03-12 2 views
1

У меня возникают проблемы с обновлением графического интерфейса на разных потоках. Мой сценарий таков:Пытаясь обновить графический интерфейс внутри темы (начато с BackgroundWorker)

У меня есть основная форма с одним флажком. В событии я начинаю работу с фоновым работником. В событии BackgroundWorker_DoWork я вызываю новый класс, который, в свою очередь, запускает новый поток. Из этого потока я пытаюсь установить Checked состояние флажка в Form1, но без везения.

До сих пор у меня есть следующий код:

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

Что такое Class1? –

+0

К сожалению, это был код, оставшийся от чего-то, что я пытался. Я попытался установить флаг из отдельного класса. Я обновил свой пост. – Riples

+1

так почему вы создаете еще один новый поток внутри 'BackgroundWorker.DoWork'? переместите свою трудоемкую задачу туда, и она должна работать нормально – Plutonix

ответ

4

Я не уверен, почему вы начинаете новый поток от рабочего фона, который уже начал новую тему для рабочего. Похоже на лишнюю ненужную нить. Рабочий поток рабочего стола не может обновлять элементы управления, которые принадлежат потоку пользовательского интерфейса, что является той же причиной, по которой ваш новый клиентский поток ручек также не может его обновить. С помощью фонового объекта вы можете обновить поток пользовательского интерфейса, подняв событие ProgressChanged рабочего фона и поместите код обновления в метод, обрабатывающий событие.

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 
+0

Возможно, стоит отметить, что вместо захвата параметра процентаProgress для другой цели существует [перегрузка ReportProgess] (https://msdn.microsoft.com/en-us/library/a3zbdb1t%28v=vs.110%29.aspx), который принимает объект как второй параметр, который вы можете использовать для передачи других данных, включая экземпляр класса с любым количеством параметров любого типа. –

+0

@AndrewMorton Я думал, что просто использовать сумму прогресса будет легче понять, но я пошел вперед и добавил использование перегруженного метода. –

+3

Вы должны использовать 'DirectCast (e.UserState, Boolean)'. Вы забыли использовать опцию Strict On? ;) –

Смежные вопросы