Backgroundworker - kleine Informationsform wird nicht aktualisiert?

  • VB.NET

Es gibt 18 Antworten in diesem Thema. Der letzte Beitrag () ist von ray.

    Backgroundworker - kleine Informationsform wird nicht aktualisiert?

    Guten Morgen,

    auf Anraten von nikeee in diesem Thema ([VB 2010] "Bitte warten..." - Fenster wird nicht richtig angezeigt / stoppt Ausführen des restlichen Codes) verwende ich nun einen Backgroundworker in meinem Programm, dass Datensätze aus einer vorhandenen Exceltabelle in eine neue kopiert.
    Beim Kopiervorgang öffnet sich eine neue Form, die anzeigt wieviele Sätze kopiert wurden.

    Die Funktion die jetzt in "Private Sub bgwDowork() Handles bgw.DoWork" aufgerufen wird (zuvor einfach beim btnclick event) sieht in etwa so aus:

    VB.NET-Quellcode

    1. WaitingScreen.lblFound.Text = intFound
    2. WaitingScreen.lblCopied.Text = intCopied
    3. WaitingScreen.Refresh()


    ... wobei WaitingScreen die neue Form ist.

    Ohne den Backgroundworker hat es wunderbar funktioniert, mit Backgroundworker allerdings werden die Zahlen / Labels nicht mehr aktualisiert sondern bleiben auf ihrem Standardwert 0.

    Was ist da los?

    Danke im Voraus und Gruß!
    Hey,

    vielen Dank für deine Antwort.

    Leider selbes Resultat, ich schreibe der Übersicht halber etwas mehr Code:

    VB.NET-Quellcode

    1. Private WithEvents bgw As New BackgroundWorker
    2. Private Sub bgwDowork() Handles bgw.DoWork
    3. ' Daten aufgreifen
    4. Dim arrListData As New ArrayList()
    5. arrListData = GetDataForExtraction()
    6. ' Extraction ausführen und Daten ausgeben
    7. ExtractDataInExcel(arrListData)
    8. End Sub
    9. Private Sub bgwCompleted() Handles bgw.RunWorkerCompleted
    10. WaitingScreen.Close()
    11. Me.Enabled = True
    12. End Sub


    Hier wird das Ganze ausgeführt:

    VB.NET-Quellcode

    1. Private Sub btnResult_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnResult.Click
    2. Me.Enabled = False
    3. bgw.RunWorkerAsync()
    4. End Sub


    Ausschnitt von "ExtractDataInExcel" die in "bgwDowork" aufgerufen wird:

    VB.NET-Quellcode

    1. WaitingScreen.lblFound.Text = intFound
    2. WaitingScreen.lblCopied.Text = intCopied
    3. WaitingScreen.Refresh() ' und/oder Application.DoEvents()



    Alles andere, also u.a. der eigentliche Kopiervorgang in dieser Funktion funktioniert wunderbar. Nur dieses updaten der Anzeige will nicht.
    Da sind wieder mehrere Threads im Spiel. Mach es mal etwa so:
    (das i wieder rausnehmen und Deine Variablen reinschreiben)

    VB.NET-Quellcode

    1. Private Sub bgwDowork() Handles bgw.DoWork
    2. ' Daten aufgreifen
    3. Dim arrListData As ArrayList()
    4. arrListData = GetDataForExtraction()
    5. WaitingScreen = New Form2 ' das Fenster in diesem Thread erstellen !!!
    6. WaitingScreen.Show() ' anzeigen
    7. For i As Integer = 0 To 100
    8. System.Threading.Thread.Sleep(100)
    9. WaitingScreen.lblFound.Text = i.ToString
    10. WaitingScreen.lblCopied.Text = i.ToString
    11. WaitingScreen.Refresh() ' und/oder Application.DoEvents()
    12. Application.DoEvents()
    13. Next
    14. ' Extraction ausführen und Daten ausgeben
    15. ExtractDataInExcel(arrListData)
    16. WaitingScreen.Close() ' und beenden
    17. End Sub
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    bist du dir sicher?

    Ich täte erwarten, der Thread stürzt ab, und die Aufgabe ist nur teilweise erledigt.

    Offensichtlich ohne Fehlermeldung.

    Check das unbedingt ab, denn sowas ist der WorstCase einer Datenverarbeitung, weil unvollständige Daten evtl. zurückgespeichert werden, und dann sind die original-Daten unwiederbringlich zerstört.
    Also eigentlich dürfte das nicht funktionieren.
    Wenn der User den WaitingScreen wegklickt, dann wird ja dennoch versucht, in ein Label desselben zu schreiben.

    Ah!

    Vlt. kommt des Users Klick gar nicht an, weil der Thread 100% busy ist.

    Und daran ändert auch Application.DoEvents nichts, weil das evtl. nur die Events des MainThreads bearbeitet.

    Kannste testen, indemde

    VB.NET-Quellcode

    1. WaitingScreen.Refresh()
    rausnimmst, und guckst, ob WaitScreen noch aktualisiert.

    Das ganze findich recht dirty. Ein Threading-Grundsatz ist: alle Controls (also auch Forms) immer nur im Mainthread erstellen. Aus dem Nebenthread kann man mit Control.BeginInvoke Nachrichten transferieren.
    Der Backgroundworker hat dafür sogar ein spezielles Event: _ProgressChanged, welches im Mainthread gefeuert wird.
    Da fällt mir doch gerade ein, dass wir die Prozeduren richtig deklarieren wollen: :D

    VB.NET-Quellcode

    1. Private Sub bgw_DoWork(ByVal sender As System.Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles bgw.DoWork
    2. End Sub
    3. Private Sub bgw_RunWorkerCompleted(ByVal sender As System.Object, ByVal e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles bgw.RunWorkerCompleted
    4. End Sub
    5. Private Sub bgw_ProgressChanged(ByVal sender As System.Object, ByVal e As System.ComponentModel.ProgressChangedEventArgs) Handles bgw.ProgressChanged
    6. End Sub
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!

    ErfinderDesRades schrieb:

    Also eigentlich dürfte das nicht funktionieren.
    Wenn der User den WaitingScreen wegklickt, dann wird ja dennoch versucht, in ein Label desselben zu schreiben.


    Wie gesagt, wenn das Label vom User geschlossen wird, geht das Fenster zu (dürfte doch bei 100% busy Thread nicht passieren oder?), die ganze Arbeit dahinter (das Extrahieren der Dateien) findet dennoch statt.


    Und daran ändert auch Application.DoEvents nichts, weil das evtl. nur die Events des MainThreads bearbeitet.


    Das ganze findich recht dirty. Ein Threading-Grundsatz ist: alle Controls (also auch Forms) immer nur im Mainthread erstellen. Aus dem Nebenthread kann man mit Control.BeginInvoke Nachrichten transferieren.
    Der Backgroundworker hat dafür sogar ein spezielles Event: _ProgressChanged, welches im Mainthread gefeuert wird.


    Wird via .Show eine Control/Form erstellt oder eine bereits existierende einfach nur angezeigt? Form2.Load wird beim Starten des Programms ausgeführt, nur Form2.Activate, .VisibilityChanged etc. werden von der anderen Thread (backgroundworker) aufgerufen.
    Ah - vlt. verwechselst du auch Typbezeichner und Objekt?

    Verwendest du Rods Zeilen?

    VB.NET-Quellcode

    1. WaitingScreen = New Form2 ' das Fenster in diesem Thread erstellen !!!
    2. WaitingScreen.Show()
    Hier erstellt New aus einem Typbezeichner ein Form2-Objekt. Anschließend wird die .ShowMethode des Objekts aufgerufen.

    Oder machst du VB-SchmuddelSchmuddel?

    VB.NET-Quellcode

    1. Form2.Show()
    Hier wird der Typbezeichner als Objekt mißverstanden, von dem man eine Methode aufrufen kann.
    Programmier-semantischer Vollblödsinn, denn ein Typbezeichner kann nix tun. Gugge auch VeryBasics
    Und bei Threading hat dieser Vollblödsinn nochma besondere Auswirkungen, hier nämlich, dassich verwirrt bin, warum dein Thread nicht crasht.

    meine Derzeitige Hypothese: Du machst gezeigten SchmuddelSchmuddel. Wenn nun der User den WaitingScreen wegklickt, erstellt der thread beim nächsten Zugriff gleich ein neues Form2-Default-Objekt.
    Da für das neue aber .Show nicht aufgerufen wird, kriegst du nix davon mit.

    ErfinderDesRades schrieb:

    Ah - vlt. verwechselst du auch Typbezeichner und Objekt?

    Verwendest du Rods Zeilen?


    Nein, nein ich verwende den Code den ich gepostet habe. Also VB-SchmuddelSchmuddel WaitingScreen.Show() (WaitingScreen ist eine im Editor erstellte Form) - wie ich oben gesagt hab.

    Und bei Threading hat dieser Vollblödsinn nochma besondere Auswirkungen, hier nämlich, dassich verwirrt bin, warum dein Thread nicht crasht.

    Da für das neue aber .Show nicht aufgerufen wird, kriegst du nix davon mit.


    So wirds dann höchstwarscheinlich sein. Wenn du jetzt noch zweimal "Vollblödsinn" weglässt kommen wir richtig gut miteinander aus, absichtlich hab ich bestimmt nicht deine heiligen VB Regeln misachtet. Zunächst sehe ich das (optische) Resultat, welches passt und erst mit mehr Hintergrundwissen wird mir klar, ob da programmiertechnisch noch Fehler drin stecken.

    Allerdings ist Form2 eine erstellte Klasse, kein Objekt (falls es da Unterschiede gibt).

    ray schrieb:

    Wenn du jetzt noch zweimal "Vollblödsinn" weglässt

    Versteh mich nicht falsch: Nirgends sage ich, du hättest Blödsinn gemacht. Der Blödsinn besteht im Design der Programmiersprache VB.Net, die im Hintergrund für jede Form-Klasse eine Default-Instanz codet, und genauso benennt, wie die Klasse.
    Dadurch kann natürlich kein Mensch den Unterschied von Klasse und Instanz schnackeln.

    Guck dir folgende Codezeilen an

    VB.NET-Quellcode

    1. Form1 = Nothing
    2. Form1.Show()

    Das ist lauffähig!

    Kriegst du da nicht Kopfschmerzen, wenn du da draufguckst? Also ich hab da allergische Reaktionen.


    Allerdings ist Form2 eine erstellte Klasse, kein Objekt (falls es da Unterschiede gibt).
    Der Unterschied ist entscheidend ;)

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „der_Kurt“ ()

    1:
    Vergleiche den Quelltext, den Du postest, mit dem, der Dir gepostet wird.
    Frage Dich, warum gibt es da Unterschiede?
    Wenn Du der Meinung bist, dass der Fremdcode besser ist, nutze ihn und bedanke Dich. Return
    Wenn nicht, mach einen expliziten Gegenvorschlag mit Deinem Code.
    GoTo 1
    ------------------
    In meinen Programmen kommt GoTo nicht vor. :thumbsup:
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!

    ErfinderDesRades schrieb:

    ...


    Ich hab mir den Link von Dir durchgelesen, allerdings hätte ich noch eine Frage:

    Indem ich WaitingScreen.Show() aufrufe erstelle ich ein Objekt auf das ich später keinen Verweis mehr habe? D.h. sobald es nicht via WaitingScreen.Close() geschlossen wird oder sich neu erstellt (weil der Benutzer das Fenster schließt, der Code aber weiterhin Methoden aufruft) werden die Instanzen im Code nicht mehr ordnungsgemäß gelöscht?

    In die gleiche Falle bin ich nämlich auch schon bei einer Excelanwendung getappt.

    Grüße!
    Jo, die unsichtbare Instanz wird nicht richtig beendet. Aber das ist nicht soo das Problem, wenn der Thread ausläuft, wird die so oder so weggeräumt.
    Ne, normalerweise ist das Hauptproblem, dass die Instanz da ist, aber unsichtbar.
    Und du hast evtl. noch eine weitere, sichtbare Instanz im MainThread, und schreibst nun Code im Nebenthread, der zu funktionieren scheint, aber man sieht nix!
    Da dran suchen VB-Progger u.U. tagelang herum.

    Deins ist mal ein Sonderfall - du hast Code geschrieben, der eigentlich crashen müsste, tut er aber nicht - und ich wundere mich ;) .