Laufende Sekunden anzeigen

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

Es gibt 9 Antworten in diesem Thema. Der letzte Beitrag () ist von ErfinderDesRades.

    Laufende Sekunden anzeigen

    Hi, ich habe eine Anwendung, bei der manche Funktionen recht lang laufen (1 - 2 Minuten).

    Da erscheint in der Title Bar "not responding" ... und da könnte man meinen, dass sich die Anwendung "aufgehängt" hat. Ich würde deshalb gern die "laufenden Sekunden" in der Form anzeigen, damit man weiß, dass das Dingens noch lebt.

    Versucht hab ich das testweise wie folgt:

    VB.NET-Quellcode

    1. Public Class frmTestTimer
    2. Dim intTimerCount As Integer
    3. Private Sub frmTestTimer_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    4. Timer1.Interval = 1000
    5. Timer1.Enabled = True
    6. End Sub
    7. Private Sub Timer1_Tick(sender As System.Object, e As System.EventArgs) Handles Timer1.Tick
    8. intTimerCount += 1
    9. txtElapsed.Text = intTimerCount.ToString
    10. End Sub
    11. Private Sub cmdEnter_Click(sender As Object, e As EventArgs) Handles cmdEnter.Click
    12. lblMessage.Text = "Command running ... "
    13. lblMessage.Update()
    14. LongRunningCommand(1000)
    15. lblMessage.Text = "Command terminated ..."
    16. End Sub
    17. Private Sub LongRunningCommand(limit As Integer)
    18. For i = 1 To limit
    19. For j = 1 To limit
    20. For k = 1 To limit
    21. Dim temp = i + j + k
    22. Next
    23. Next
    24. Next
    25. End Sub
    26. End Class


    Der Timer läuft auch sehr hübsch und zeigt jeweils die Anzahl der verstrichenen Sekunden an.

    Nur wenn man das LongRunningCommand started, hält der Timer sofort an ... Offensichtlich werden keine TimerTicks mehr generiert ...

    Wenn das LongRunningCommand endet, läuft der Timer aber wieder weiter.

    Kann man erreichen, dass die laufenden Sekunden angezeigt werden, auch während so ein LongRunningCommand aktiv ist ?

    LG
    Peter
    Dein Problem ist, dass dieser LongRunningCommand in deinem GUI Thread läuft. Damit werden andere Nachrichten in den Mainthread erst abgearbeitet, wenn der LongRunningCommand fertig ist. Vor allem GUI Updates werden erst durchgeführt, wenn der Mainthread nicht in einer langen Schleifen beschäftigt ist.
    Die Lösung nennt sich asynchrone Verarbeitung. Je nach .NET Version gibt es verschiedene Möglichkeiten aufwändige Arbeiten asynchron durchzuführen. Ab NET 4 gibt es Async und Await. Einfach mal danach googlen.
    Dann klappt es auch mit dem Update der GUI ;)
    Das ist meine Signatur und sie wird wunderbar sein!
    Jou.

    Peter329 schrieb:

    LongRunningCommand
    Pack diesen Befehl in einen anderen Thread.
    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!
    Vermeide dabei den Backgroundworker, und machs gleich richtig mit Async, oder halt oldFashioned mit Delegate.BeginInvoke().
    zu async: codeproject.com/Articles/10296…ithout-any-additional-Lin
    Async hat eine Kommunikation des Gui-Threads mit dem NebenThread vorgesehen, bei Delegate.BeginInvoke() muss mans selbst basteln, wenn mans braucht.

    Brauchst du's?
    Also ... ich brauche eigentlich nicht viel. Aber das was ich brauche, ist essentiell. :)

    Ich muss ein langlaufende Prozedur - nennen wir sie Hugo(...) - asynchron starten können, damit mein Timer weiter tickelt. Die Prozedur Hugo() selbst ist recht tief verschachtelt, falls das eine Rolle spielen sollen.

    Außerdem muss ich verlässlich wissen, wann Hugo() endet. Denn in dieser Zeit dürfen keine anderen Prozeduren angestoßen werden. (Weil es nicht ganz so gut wäre, wenn etwa während einer Sicherung andere Aktivitäten zuschlagen, mit denen die Dateistruktur verändert wird).

    Weiterhin möchte ich wissen, wie Hugo() endet. Es muss also ein Rückgabewert geliefert werden.

    Das ist alles ... und das sollte doch eigentlich nicht gar so schwer zu realiseren sein.

    Auf den ersten Blick scheint mir Async / Await genau das zu leisten ... oder bin ich auf dem Holzweg ?

    LG
    Peter
    @Peter329 Probier mal dies aus.
    Form, Button, Label Timer:
    Spoiler anzeigen

    VB.NET-Quellcode

    1. Imports System.Threading.Tasks
    2. Imports System.Threading.Thread
    3. Public Class Form1
    4. Private value As Long = 0
    5. Private Async Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    6. Me.Timer1.Interval = 20
    7. Me.value = 0
    8. Me.Timer1.Start()
    9. Me.Button1.Location = New Point(16, 12)
    10. Await Task.Run(Sub() Me.MoveButton(200, 1))
    11. End Sub
    12. Private Async Sub MoveButton(count As Integer, stp As Integer)
    13. Await Task.Run(Sub()
    14. Dim pt = Me.Button1.Location
    15. For i = 0 To count - 1
    16. pt.Offset(stp, stp)
    17. Me.BeginInvoke(Sub() Button1.Location = pt)
    18. Sleep(20)
    19. Next
    20. Me.Timer1.Stop() ' dies wäre die Info an den Main-Thread
    21. End Sub)
    22. End Sub
    23. Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
    24. Me.BeginInvoke(Sub()
    25. Me.Label1.Text = Me.value.ToString
    26. Me.value += 1
    27. End Sub)
    28. End Sub
    29. End Class
    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!

    Peter329 schrieb:

    Also ... ich brauche eigentlich nicht viel. Aber das was ich brauche, ist essentiell....

    hahaha! :D

    Und dann kommt fast die komplette Liste aller Problematiken, die bei Threading zu lösen sind (fehlt eiglich nur noch ProgressReport und Exceptionhandling).

    Aber du hast recht - Async ist genau dafür gemacht, und mein CodeProject-Artikel kaut das alles auch durch. Wenner dir was bringt - über ein CP - Up-Rating würde ich mich freuen.
    @RFG

    Schönen Dank für dein Code Beispiel ... der Button1 rutscht munter über den Bildschirm, während das Label1 unbeirrt weiter zählt. So kann man also dafür sorgen, dass die Kiste asynchron läuft. Schon beeindruckend, wie einfach das geht.

    Ein Rückgabewert kann wie von dir vorgeschlagen das Abschalten des Timers sein ... Man könnte alternativ eine Variable in einem Datenmodul setzen ... das scheint also auch kein Problem zu sein. Klingt soweit alles recht gut.

    Was mich halt sehr stört: das Fenster der Form1 bleibt offen ..., d.h. man kann weiter alle Funktionen aufrufen! Als Lösung fällt mir spontan jetzt nur ein, alle Ereignisroutinen (also jeden Button, jeden Doppelclick, etc.) mit einer Abfrage zu versehen und zu deaktivieren, etwa

    VB.NET-Quellcode

    1. If Timer1.Enabled Then Exit Sub


    Oder noch schlimmer: alle Controls zu disablen und hinterher wieder zu enablen.

    Hmm ... so richtig happy bin ich mit dieser Aussicht dann doch wieder nicht ...

    @EDR

    Tja ... da werde ich dann vielleicht doch noch tiefer in die Segnungen des Mr. PoorEnglish einsteigen müssen ... :) Ich würde dem Kerl ja gern wunschgemäß eine positive Wertung zukommen lassen ... aber es schein wohl so zu sein, dass ich mich da erst mal registrieren muss. Ist das richtig ?

    LG
    Peter

    Peter329 schrieb:

    mit dieser Aussicht
    Du musst einfach eine Prozedur aufrufen oder ein Event absetzen. Einfacher geht es nun wirklich nicht.
    Du musst lediglich beachten, dass dieser Aufruf aus dem Thread heraus kommt.
    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!
    k.A.
    ich bin da registriert, und dann sind die Funktionen halt da.
    Als unregistrierter: Hast du da die Möglichkeit, das Sample zu downloaden?

    Zum Problem mit dem weiterhin aktiven Form: ja, alle relevante problematische Funktionalität musste solange disablen - das ist numal Threading.
    Das muss man halt von vornherein sich richtig überlegen: Was darf aktiv bleiben, was nicht, und eine Möglichkeit zum Abbrechen braucht man eiglich auch immer.

    Disablen tut man übrigens eher nicht mit Add-/Remove-Handler, sondern man setzt meist Control.Enabled = False/True.