Gewinde Laden (Warte) Bildschirm
-
11-07-2019 - |
Frage
Ich bin für eine generische Methode einer Warte Bildschirm bei langen Operationen zu implementieren. Ich habe vor dem Einfädeln ein paar Mal benutzt, aber ich habe das Gefühl, dass ich es umgesetzt entweder sehr schlecht oder mit viel zu viel Aufwand (und Kopieren / Einfügen - der Horror).
Ich möchte dies als generisch halten und einfach wie möglich, so dass ich nicht haben, um Lasten von BackgroundWorker
s zu implementieren alle Arten von Mist Handhabung, die Dinge schwer zu halten.
Hier ist, was ich tun möchte - bitte beachten Sie, diese von unterschiedlich sein können, was tatsächlich möglich / Best Practice / was auch immer - mit VB.NET, Framework 2.0 (also keine anonymen Methoden):
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
Die Anwendung ist nun vollständig single-threaded, so läuft alles auf dem GUI-Thread. Ich muss in der Lage, Objekte in DoSomethingTakingALongTime()
zuzugreifen, ohne Cross-Thread Ausnahmen zu bekommen. Der GUI-Thread wartet auf ein bestimmtes Verfahren (das eine lange Zeit in Anspruch nimmt) in Anspruch nehmen, während das LoadingScreen
Formular ansprechbar bleiben sollte (es ist animiert / hat ein progressbar / etc.).
Ist das ein doable / guter Ansatz oder sehe ich auf diese Weise zu einfach? Was ist die beste Praxis diesbezüglich? Und vor allem: Wie konnte ich ein solches System implementieren? Wie ich bereits erwähnt, habe ich sehr wenig Erfahrung mit einem Gewinde, so sanft bitte: -)
Lösung
Ihr Problem ist, dass Ihr einen Querfaden Ausnahme bekommen, wenn Ihr versucht, Ihre Worker-Thread Daten auf Ihren UI-Thread zu übergeben. was Sie tun müssen, ist InvokeRequired zu überprüfen und BeginInvoke bevor die Bedienelemente des ui einstellen, so dass Sie wie so nicht den Fehler:
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
nur den New EventHandler
Teil an den Event-Handler ändern Ihre Verwendung.
Auch denke ich mit einem Hintergrund Arbeiter ist keine schlechte Methode für Ihre Arbeiter Klassen, nur eine Klasse für Ihre Arbeit schaffen und den Hintergrund Arbeiter verwenden das Einfädeln Sachen ein bisschen wie dies zu tun:
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
Disclaimer .. etwas verrostet mit vb, aber Sie sollten die Idee.
Andere Tipps
In dem Thread verwenden Application.Run (yourform) zu bekommen, was Sie wollen.
Beachten Sie, dass Sie benötigen, um das Formular zu signalisieren, sich zu schließen, irgendwie.
Ich hoffe, dass Sie nicht finden, diese nicht hilfreich - aber ich würde fragen, warum Sie einen Gewindewarte Bildschirm wollen würden? Der Grund für die in erster Linie mit Gewinde ist, so dass der UI ansprechbar bleibt und lange Operationen werden im Hintergrund durchgeführt.
Ansonsten könnte man genauso gut haben nur eine ProgressBar auf FormLoading Kontrolle und haben DoSomethingTakingALongTime sie in regelmäßigen Abständen zu aktualisieren. Dies würde nicht Threads braucht überhaupt.