​Progressbar und gefrorene Gui

  • VB.NET
  • .NET (FX) 4.5–4.8

Es gibt 23 Antworten in diesem Thema. Der letzte Beitrag () ist von Haudruferzappeltnoch.

    ​Progressbar und gefrorene Gui

    Hallo,

    Ich habe ein Form mit einem mehr oder minder langem Ladevorgang (~ 1-4 sek.)

    Jetzt hab ich da eine Progressbar zu gebastelt nur um festzustellen: Anzeige geht ja gar nicht, weil die Gui derweil eingefroren ist.
    Das war auch so in etwa geplant, also ich wollte den Prozess nicht in einen anderen Task auslagern bzw. die Form soll auch nicht sonderlich bedienbar während des Ladens sein.

    Da ist jetzt die Frage muss ich trotzdem das in einen neuen Task schieben um den Fortschritt anzuzeigen?
    Oder gäbe es eine Möglichkeit den Fortschritt sozusagen "aus einem anderen Task heraus anzuzeigen" Ich weiß nicht, dass man da n Bildchen drüberhängt oder so :D ?

    Viele Grüße
    @Haudruferzappeltnoch Du musst den Ladevorgang nebenläufig in einem anderen Thread abarbeiten, das Zauberwort heißt Async Await.
    docs.microsoft.com/de-de/dotne…ing-guide/concepts/async/
    Async, Await und Task
    Wenn dieser Vorgang selbst weiß, wie lange er dauert, kann er Events an das Hauptfenster senden.
    Wenn nicht, kannst Du im Hauptfenster auch eine ProgressBar mit dem Style Marquee anzeigen.
    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!
    Wenn es mehrere Schritte sind, die immer abgearbeitet werden müssen, könntest Du das im GUI-Thread machen, indem Du die ProgressBar oder alternativ ein Label per Refresh zum GUI-Anzeigen-Update zwingst.
    Also quasi

    Quellcode

    1. Sub LängereAufgabe()
    2. SetzeStatusControlAufAnfang()
    3. LangAndauernderSchritt1()
    4. ZwingDasControlZumAnzeigenupdate()
    5. LangAndauernderSchritt1()
    6. ZwingDasControlZumAnzeigenupdate()
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.
    Clientbereich ist der Inhalt des Controls.
    Etwa bei einem Form der TitelBalken - der gehört nicht zum Client-Bereich.
    Oft ist der ClientBereich mit den Gesamt-Abmassen des Controls identisch.

    Zu deim Problem sehe ich 2 Lösungs-Ansätze:

    ErfinderDesRades schrieb:

    Problem ist, dass User sehr findig darin sind, während der Wartezeit iwas zu clicksen, was dann nach Ablauf zu Chaos oder Absturz führt.
    Für solch einen Fall erweitere ich den o.g. Pseudocode bei mir auf:

    Quellcode

    1. Sub LängereAufgabe()
    2. DeaktiviereDasForm()
    3. SetzeStatusControlAufAnfang()
    4. LangAndauernderSchritt1()
    5. ZwingDasControlZumAnzeigenupdate()
    6. LangAndauernderSchritt1()
    7. ZwingDasControlZumAnzeigenupdate()
    8. ReaktiviereDasForm()


    ##########

    was allerdings das Programm nicht davon abhält, die Eingaben trotzdem zu verarbeiten :S

    ##########

    Es sei denn, man verwendet meinen bei mir beliebten Delay-Workaround

    Quellcode

    1. Async Sub LängereAufgabe()
    2. DeaktiviereDasForm()
    3. SetzeStatusControlAufAnfang()
    4. LangAndauernderSchritt1()
    5. ZwingDasControlZumAnzeigenupdate()
    6. Await Threading.Tasks.Task.Delay(1)
    7. ReaktiviereDasForm()

    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „VaporiZed“ ()

    Haudruferzappeltnoch schrieb:

    Was ist denn der Clientbereich?
    Im Client-Bereich kannst Du "arbeiten": Malen (PictureBox), andere Controls platzieren (GroupBox) usw.
    Wie der @ErfinderDesRades meint:
    Bei einer PictureBox sind Size und ClientSize identisch. Bei einer GroupBox nicht.
    Wenn Du die Größe einer Form an ihren Inhalt anpassen musst, kannst Du auch die Größe des Client-Bereiches vorgeben, der Rest passt sich dann an.
    Wenn allerdings Dein Programm auf verschiedenen Betriebssystemen laufen soll (W7, W8, W10), dann musst Du bei der Größenanpassung auch die Randbreite der Form mit berücksichtigen, die ist ggf. unterschiedlich.
    Gugst Du SystemInformation.BorderSize, SystemInformation.CaptionHeight usw.
    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!

    RodFromGermany schrieb:

    Gugst Du SystemInformation.BorderSize, SystemInformation.CaptionHeight usw.
    Was ich manchmal für'n Sch… gecodet habe, um herauszufinden, wie groß eine Fenster-Titelleiste ist X/
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.

    VaporiZed schrieb:

    wie groß eine Fenster-Titelleiste ist
    Ich hatte das grade, ein Kollege hat ein W8.1 laufen, da war meine ganze Fensteraufteilung flöten, eigentlich nur ein FlowLayoutPanel mit mehreren UserControls zur Statusanzeige.
    Ich hatte die unter W7 eingerichtet, unter W8 waren da zwei Scrollbalken. Nun nicht mehr.
    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!
    Naja asynchron ist meine Anwendung ja (noch) nicht. Also kann da mit rumklicken nicht unbedingt was kaputt gehen.

    Jetzt wollte ich @VaporiZeds Refresh Vorschlag ausprobieren, musste jedoch feststellen das meine ProgressBar auch schon von ganz allein die Anzeige updatet.
    Im Original benutze ich eine CircularProgressBar und mit jener hängt es auch im Test. Mit Refresh konnte ich da allerdings auch nichts ändern.

    VB.NET-Quellcode

    1. Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    2. CircularProgressBar1.Value = 0
    3. For i = 1 To 100
    4. System.Threading.Thread.Sleep(100)
    5. CircularProgressBar1.Value = i
    6. Next
    7. End Sub


    Es wird wohl auf die Ausführung im Nebenthread hinauslaufen oder ich probiers mit der normalen ProgressBar mal im Original. Danke euch

    Haudruferzappeltnoch schrieb:

    VB.NET-Quellcode

    1. System.Threading.Thread.Sleep(100)
    Das ist ja das Beispiel schlechthin, womit Du Deine GUI ausbremst.
    Da hat er ha ühaupt keine Gelegenheit, irgendwas darzustellen.
    Wenn Du in diesem Zustand ein anderes Fenster (Notepad oder so) auf Deine GUI legst ind dann wegnimmst, kannst Du das gut beobachten.
    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!
    du kannst auch nochmal post#3 studieren - da sind zwei saubere Lösungen vorgeturnt.

    Von Vaporizeds ZwingDasControlZumAnzeigenupdate() bin ich ziemlich unüberzeugt.
    Anzeige sollte nicht erzwungen werden, sondern nur mit .Invalidate() angestossen.



    Zur Progressbar: Ja, die Standard-PB ist eine Ausnahme - das einzige mir bislang bekannte Control, was Threadsicher ist.
    Klar kann CircularProgressBar das nicht.
    @RodFromGermany Meinst du den Editor? Wenn ich den aufrufe, während das Programm läuft sehe ich nichts Besonderes.

    @ErfinderDesRades Ich weiß, ging mir halt drum vorher in Erfahrung zu bringen ob ich den Nebenthread vermeiden könnte.

    Die ProgressBar funktioniert auf jeden Fall schonmal besser auch im Original, allerdings nicht vollständig, woran könnte das liegen?

    VB.NET-Quellcode

    1. Private Sub CountdownTickOrClick(sender As Object, e As EventArgs) Handles timeCountdown.Tick, btCountdown.Click
    2. Dim nr As Integer
    3. a = True
    4. nr = Init()
    5. If Not nr = 0 Then
    6. ProgressBar1.Visible = True
    7. Dim LBrow As DataSet1.dtLBRow
    8. Dim progresscounter As Double = 1
    9. For Each row In DS1.dtLB.Rows
    10. LBrow = DirectCast(row, DataSet1.dtLBRow)
    11. If a Then
    12. Prozess1() 'Hier arbeitet überwiegend LINQ
    13. Else
    14. Prozess2() 'Hier arbeitet überwiegend LINQ
    15. End If
    16. ProgressBar1.Value = CInt(Math.Round(80 * progresscounter / CDbl(DS1.dtLB.Rows.Count))) '''Diese Anzeigen werden nicht übernommen
    17. progresscounter = progresscounter + 1
    18. Next 'Gesamtdauer ~3sek
    19. ToE() 'Datenbankabfrage MSSQL
    20. ProgressBar1.Value = 0
    21. ProgressBar1.Visible = False
    22. End Sub
    23. Private Sub ToE()
    24. ... 'DBAbfrage1 Dauer ~1sek
    25. ProgressBar1.Value = 90 '''Diese Anzeige wird direkt übernommen
    26. ... 'DBAbfrage2 Dauer ~1sek
    27. ProgressBar1.Value = 100 '''Diese Anzeige wird direkt übernommen
    28. ...
    29. End Sub

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „Haudruferzappeltnoch“ ()

    Haudruferzappeltnoch schrieb:

    Meinst du den Editor?
    Nö.
    GUI = Graphical User Interface, das ist die Benutzeroberfläche Deines Programms, in dem dieser Code läuft.
    Nach dem Button-Click hast Du doch 10 Sekunden Zeit festzustellen, was da passiert.
    Öffne das Notepad, maximiere es und schließe es wieder.
    Danach müsste innerhalb der besagten 10 Sekunden Deine GUI weiß sein.
    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!

    Haudruferzappeltnoch schrieb:

    aber das ist sie auch nach dem starten schon.
    Der Button sollte dabei auch verschwinden.
    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!

    Haudruferzappeltnoch schrieb:

    ist das denn relevant?
    Eigentlich nicht, das dient nur zum Verständnis, was während des Sleep(..) passiert bzw. nicht passiert.
    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!
    Naja ich ging mal stark davon aus, dass der Thread in der Zeit nicht arbeitet bzw. streng genommen mit "nicht arbeiten" beschäftigt ist.

    Dass das Fenster nicht reagiert auf Knopfdrücke in der Zeit ist mir klar, aber das Erscheinungsbild ändert sich nicht.
    Und nachdem die GUI wieder frei ist, werden Knopfdrücke, die während des Sleeps getätigt wurden abgearbeitet
    Ich habs jetzt dann doch versucht es in einen Nebenthread zu legen. Scheitere jedoch bei der dritten Methode aufgrund eines Fehlers.

    VB.NET-Quellcode

    1. Private Async Sub CountdownTickOrClick(sender as Object, e as EventArgs) Handles timeCountdown.Tick, btCountdown.Click,
    2. ...
    3. Await Task.Run(Sub() ToE())
    4. ...
    5. End Sub
    6. Private Sub ToE()
    7. Dim Importsql = ""
    8. Dim Import2sql = ""
    9. DS1.dt1.Clear()
    10. Using DBZ1 As New DBZugriff(Importsql)
    11. DBZ1.DBAdapter.Fill(DS1.dt1)
    12. End Using
    13. DS1.dt2.Clear()
    14. Using DBZ2 As New DBZugriff(Import2sql)
    15. DBZ2.DBAdapter.Fill(DS1.dt2)
    16. End Using
    17. End Sub

    Ohne Await läuft die Methode. Mit Await gibts Probleme mit dem Fill-Befehl denke ich, da das DGV meckert (er springt nicht ins Studio zum Debuggen):