Экран потоковой загрузки (ожидания)
-
11-07-2019 - |
Вопрос
Я ищу универсальный метод для реализации экрана ожидания во время длительных операций.Я уже несколько раз использовал потоковую обработку, но у меня такое ощущение, что я реализовал это либо очень плохо, либо со слишком большими хлопотами (а копирование / вставка - ужас!).
Я хочу, чтобы это было как можно более общим и простым, поэтому мне не придется реализовывать множество BackgroundWorker
занимается всяким дерьмом, усложняя обслуживание.
Вот что я хотел бы сделать - пожалуйста, обратите внимание, что это может отличаться от того, что на самом деле возможно / лучшая практика / что угодно - используя VB.NET, Framework 2.0 (поэтому никаких анонимных методов):
Private Sub HandleBtnClick(sender as Object, e as EventArgs) Handles Button.Click
LoadingScreen.Show()
'Do stuff here, this takes a while!'
Dim Result as Object = DoSomethingTakingALongTime(SomeControl.SelectedObject)
LoadingScreen.Hide()
ProcessResults(Result)
End Sub
Приложение теперь полностью однопоточное, поэтому все выполняется в потоке GUI.Мне нужно иметь возможность доступа к объектам в DoSomethingTakingALongTime()
без получения исключений из разных потоков.Поток графического интерфейса ожидает завершения выполнения некоторого метода (что занимает много времени), в то время как LoadingScreen
Форма должна оставаться отзывчивой (она анимирована / имеет панель выполнения / и т.д.).
Является ли это выполнимым / хорошим подходом или я вижу этот способ слишком упрощенным?Какова наилучшая практика в этом вопросе?И самое главное:как я мог бы внедрить такую систему?Как я уже упоминал, у меня очень мало опыта работы с потоками, поэтому, пожалуйста, будьте помягче :-)
Решение
Ваша проблема в том, что вы получаете исключение перекрестного потока при попытке передать данные вашего рабочего потока в ваш поток пользовательского интерфейса.что вам нужно сделать, это проверить InvokeRequired и begininvoke перед установкой элементов управления в вашем пользовательском интерфейсе, чтобы вы не получили ошибку, подобную этой:
Private Sub work_CrossThreadEvent(ByVal sender As Object, ByVal e As System.EventArgs) Handles work.CrossThreadEvent
If Me.InvokeRequired Then
Me.BeginInvoke(New EventHandler(AddressOf work_CrossThreadEvent), New Object() {sender, e})
Return
End If
Me.Text = "Cross Thread"
End Sub
просто измените New EventHandler
часть используемого вами обработчика событий.
Также я думаю, что использование фонового рабочего не является плохим методом для ваших рабочих классов, просто создайте класс для своей работы и используйте фонового рабочего для выполнения потоковых операций примерно так:
Public MustInherit Class Worker
Protected WithEvents worker As BackgroundWorker
Public Sub New()
worker = New BackgroundWorker()
worker.WorkerReportsProgress = True
worker.WorkerSupportsCancellation = True
End Sub
Public Sub Start()
If (Not worker.IsBusy AndAlso Not worker.CancellationPending) Then
worker.RunWorkerAsync()
End If
End Sub
Public Sub Cancel()
If (worker.IsBusy AndAlso Not worker.CancellationPending) Then
worker.CancelAsync()
End If
End Sub
Protected MustOverride Sub Work()
Private Sub OnDoWork(ByVal sender As Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles worker.DoWork
Work()
End Sub
Public Event WorkCompelted As RunWorkerCompletedEventHandler
Private Sub OnRunWorkerCompleted(ByVal sender As Object, ByVal e As RunWorkerCompletedEventArgs) Handles worker.RunWorkerCompleted
OnRunWorkerCompleted(e)
End Sub
Protected Overridable Sub OnRunWorkerCompleted(ByVal e As RunWorkerCompletedEventArgs)
RaiseEvent WorkCompelted(Me, e)
End Sub
Public Event ProgressChanged As ProgressChangedEventHandler
Private Sub OnProgressChanged(ByVal sender As Object, ByVal e As ProgressChangedEventArgs) Handles worker.ProgressChanged
OnProgressChanged(e)
End Sub
Protected Overridable Sub OnProgressChanged(ByVal e As ProgressChangedEventArgs)
RaiseEvent ProgressChanged(Me, e)
End Sub
End Class
Public Class ActualWork
Inherits Worker
Public Event CrossThreadEvent As EventHandler
Protected Overrides Sub Work()
'do work here'
WorkABit()
worker.ReportProgress(25)
WorkABit()
worker.ReportProgress(50)
WorkABit()
worker.ReportProgress(75)
WorkABit()
worker.ReportProgress(100)
End Sub
Private Sub WorkABit()
If worker.CancellationPending Then Return
Thread.Sleep(1000)
RaiseEvent CrossThreadEvent(Me, EventArgs.Empty)
End Sub
End Class
Отказ от ответственности..немного подзабылось с vb, но вы должны уловить идею.
Другие советы
В вашем потоке используйте Application.Запустите (yourform), чтобы получить то, что вы хотите.
Обратите внимание, что вам нужно каким-то образом подать сигнал форме о закрытии самой себя.
Я надеюсь, вы не сочтете это бесполезным, но я бы задался вопросом, зачем вам нужен экран ожидания с потоковой передачей?Причина использования многопоточности в первую очередь заключается в том, что пользовательский интерфейс остается отзывчивым, а длительные операции выполняются в фоновом режиме.
В противном случае, вы могли бы с таким же успехом просто иметь ProgressBar в вашем элементе управления FormLoading и иметь DoSomethingTakingALongTime для периодического обновления его.Для этого вообще не понадобились бы потоки.