Threading: Wait-Dialog Text zwischendurch ändern

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

Es gibt 25 Antworten in diesem Thema. Der letzte Beitrag () ist von RodFromGermany.

    Threading: Wait-Dialog Text zwischendurch ändern

    Hallo zusammen.

    Ich hab' mit Threading nicht so viel am Hut, daher hier meine Problemstellung:

    Bei mir gibt's einen kleinen "IsBusy-Dialog" (dlgIsBusy) - also eine Form, wo ein Ladebalken durch rennt (Marquee), einem Label für eine Beschreibung und einen Titeltext:
    Spoiler anzeigen


    VB.NET-Quellcode

    1. Imports System.Threading, System.ComponentModel
    2. Partial Public Class dlgIsBusy : Inherits Form
    3. Public Sub New()
    4. InitializeComponent()
    5. End Sub
    6. Public Sub New(Text As String, Description As String)
    7. InitializeComponent()
    8. Me.Text = Text
    9. lblDesc.Text = Description
    10. End Sub
    11. End Class



    Angesprochen wird das so (und funzt auch einwandfrei):

    VB.NET-Quellcode

    1. Using New Threading.WaitDlg(New dlgIsBusy("Fenstertitel", $"Beschreibung (Label)"))
    2. 'Tu irgendwas
    3. End Using


    Die Klasse WaitDlg:
    Spoiler anzeigen

    VB.NET-Quellcode

    1. Public Class WaitDlg : Implements IDisposable
    2. Private _waitDlg As Form
    3. Public Sub New(dlg As Form)
    4. _waitDlg = dlg
    5. Dim thr As New Thread(New ThreadStart(AddressOf workerThread))
    6. With thr
    7. .IsBackground = True
    8. .SetApartmentState(ApartmentState.STA)
    9. .Start()
    10. End With
    11. End Sub
    12. Public Sub Dispose() Implements IDisposable.Dispose
    13. _waitDlg.Invoke(New MethodInvoker(AddressOf stopThread))
    14. End Sub
    15. Private Sub stopThread()
    16. _waitDlg.Close()
    17. End Sub
    18. Private Sub workerThread()
    19. With _waitDlg
    20. .TopMost = True
    21. End With
    22. Application.Run(_waitDlg)
    23. End Sub
    24. End Class


    Hab ich irgendwie die Möglichkeit den Titel oder die Beschreibung zwischendurch zu ändern (innerhalb des Using-Blocks)?
    Also in etwa so:

    VB.NET-Quellcode

    1. Using New Threading.WaitDlg(New dlgIsBusy("Fenstertitel", $"Beschreibung (Label)"))
    2. 'Tu irgendwas
    3. 'hier Beschreibung ändern
    4. 'Tu was anderes
    5. 'hier Beschreibung ändern
    6. 'etc..
    7. End Using

    VB.NET-Quellcode

    "Na, wie ist das Wetter bei dir?"
    "Caps Lock."
    "Hä?"
    "Shift ohne Ende!" :thumbsup:
    @tragl Sende vom Prozess an den Dialog ein Event.
    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:

    an den Dialog ein Event.

    Steh' grad aufm Schlauch. Müsste ich dann wohl erst deklarieren - sowas wie
    Using dlgWait As New Threading.WaitDlg(New dlgIsBusy("Fenstertitel", $"Beschreibung (Label)")) ?
    Erst dann kann ich ihn ja theoretisch ansprechen oder?

    Edit: hmm, wohl eher nicht, damit kann ich ja nur die Klasse WaitDlg ansprechen, ich bräuchte aber ja die Form,
    da hätte ich schon was vorbereitet:

    VB.NET-Quellcode

    1. Public Sub UpdateTitleAndDescription(Description As String, Optional Title As String = "")
    2. lblDesc.Text = Description
    3. If Title <> "" Then Me.Text = Title
    4. End Sub



    "Na, wie ist das Wetter bei dir?"
    "Caps Lock."
    "Hä?"
    "Shift ohne Ende!" :thumbsup:

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

    @tragl Sorry, das ist ja anders herum.
    Du willst den Dialog in einem separaten GUI-Thrfead laufen lassen und vom Hauptprogramm aus den Dialog aktualisieren.
    Dann instanziiere den Dialog nicht anonym, sondern pack die Instanz in eine Variable und weise der die neuen Texte zu.
    Feddich.
    Ich hab das so gelöst:

    C#-Quellcode

    1. /// <summary>
    2. /// Start des Progress-Dialogs in einem anderen Thread
    3. /// </summary>
    4. private void ShowProgress()
    5. {
    6. // Zwei Bars anzeigenoder nicht
    7. this.DlgProgress = new FrmProgress();
    8. this.DlgProgress.BreakHandler += new EventHandler<EventArgs>(this.DlgProgress_BreakHandler);
    9. Application.Run(this.DlgProgress);
    10. }

    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!

    tragl schrieb:

    Leider nein...
    Lies mal den Fehler ganz laut vor.
    Bei 717 Posts im Forum solltest Du nur etwa 20 Sekunden brauchen, um das aufzulösen :!:
    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: Hab schon verstanden, dass das der falsche Thread ist. Aber wie komm ich auf den des Dialogs?
    Wie gesagt, die ganze Geschichte mit dem Threading ist Neuland für mich :( Wobei ich das sicher später mal öfter brauchen werde, um die Performance
    ggf. noch zu verbessern. Aber soweit isses noch nicht
    "Na, wie ist das Wetter bei dir?"
    "Caps Lock."
    "Hä?"
    "Shift ohne Ende!" :thumbsup:
    @tragl In meinem C#-Snippet gibt es die Variable DlgProgress.
    Der hat eine Methode zum Reinreichen der anzuzeigenden Information.
    Da der Dialog in einem anderen Thread läuft, musst Du die Ausgabe invoken.
    Dazu findest Du hier im Forum beliebig viele Beispiele.
    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!
    Für Laien wie mich sieht das alles sehr Pfeil-Rücken-Brust-Auge mit dem ganzen Dialogzeugsl aus.
    Worin besteht der Zweck des ganzen Systems? Weil: Selbst wenn man das bestehende so zum Laufen wie gewünscht bekommt: Warum wird nicht einfach einem normalen Gui-Thread-Dialogfenster eine langwierige Aufgabe als z.B. Parameter oder so mit übergeben, der Dialog startet den kritischen/langanhaltenden Prozess nebenläufig und lässt sich von dem regelmäßig Updates geben?
    Damit bestünde das ganze Konstrukt aus einem MainForm, einem WarteDialogForm, welches per ShowDialog ganz normal aufgerufen wird, und einem Async/Await-Aufruf mit der langwierigen Aufgabe.
    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:

    welches per ShowDialog ganz normal aufgerufen wird, und einem Async/Await-Aufruf mit der langwierigen Aufgabe.

    ist ja nicht so machbar mit Daten beiläufig laden etc. Ich hab die Dinger auch eher selten verbaut weil das Meiste zügig durch läuft.
    Außerdem müsste ich ja dann für jede länger dauernde Aufgabe was basteln. So hab ich nen vorgefertigten "Dialog" mit nem "Ladebalken" und möchte ja nur bei Bedarf den Text anpassen können - sodass ich auch sehe, was da gerade im Hintergrund passiert.

    @RodFromGermany: Hatte ich schon probiert, funzt nicht:


    UpdateTitleAndDescription ist eine Public Sub auf der Form dlgIsBusy:

    VB.NET-Quellcode

    1. Imports System.Threading, System.ComponentModel
    2. Partial Public Class dlgIsBusy : Inherits Form
    3. Public Sub New()
    4. InitializeComponent()
    5. End Sub
    6. Public Sub New(Text As String, Description As String)
    7. InitializeComponent()
    8. Me.Text = Text
    9. lblDesc.Text = Description
    10. End Sub
    11. Public Sub UpdateTitleAndDescription(Description As String, Optional Title As String = "")
    12. lblDesc.Text = Description
    13. If Title <> "" Then Me.Text = Title
    14. End Sub
    15. End Class

    "Na, wie ist das Wetter bei dir?"
    "Caps Lock."
    "Hä?"
    "Shift ohne Ende!" :thumbsup:

    tragl schrieb:

    ist ja nicht so machbar mit Daten beiläufig laden etc.
    Warum nicht? Nur GUI-Änderungen müssen im GUI-Thread laufen. Welches Datenladen darf nicht nebenläufig gemacht werden?
    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.

    tragl schrieb:

    Hatte ich schon probiert, funzt nicht
    OMG.
    Wenn Du das so nicht kannst, schreibe einfachen Code, den Du selbst verstehen kannst.
    Form1 mit 2 Button, Form2 mit 1 Label

    VB.NET-Quellcode

    1. Imports System.Threading
    2. Public Class Form1
    3. Private ThreadForm As Form2
    4. Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    5. Dim th = New Thread(AddressOf StartDialog)
    6. th.Start()
    7. End Sub
    8. Private Sub StartDialog()
    9. If ThreadForm Is Nothing Then
    10. ThreadForm = New Form2
    11. Application.Run(ThreadForm)
    12. End If
    13. End Sub
    14. Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
    15. If ThreadForm IsNot Nothing Then
    16. For i = 1 To 100
    17. Thread.Sleep(100)
    18. ThreadForm.SetText(i.ToString)
    19. Next
    20. End If
    21. End Sub
    22. Private Sub Form1_FormClosed(sender As Object, e As FormClosedEventArgs) Handles MyBase.FormClosed
    23. If ThreadForm IsNot Nothing Then
    24. ThreadForm.Close()
    25. End If
    26. End Sub
    27. End Class

    VB.NET-Quellcode

    1. Public Class Form2
    2. Public Sub SetText(msg As String)
    3. If Me.InvokeRequired Then
    4. ' ruft sich selbst auf
    5. Dim ac = New Action(Of String)(AddressOf SetText)
    6. Me.Invoke(ac, msg)
    7. Return
    8. End If
    9. Me.Label1.Text = msg
    10. End Sub
    11. Public Overloads Sub Close()
    12. If Me.InvokeRequired Then
    13. ' ruft sich selbst auf
    14. Dim ac = New Action(AddressOf Close)
    15. Me.Invoke(ac)
    16. Return
    17. End If
    18. MyBase.Close()
    19. End Sub
    20. 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!
    @RodFromGermany:
    Danke! Und Sorry, geht nun

    VB.NET-Quellcode

    1. Public Sub UpdateDescription(Description As String)
    2. If Me.InvokeRequired Then
    3. Dim act As New Action(Of String)(AddressOf UpdateDescription)
    4. Me.Invoke(act, Description)
    5. End If
    6. lblDesc.Text = Description
    7. End Sub


    Intellisense ist in den Dingen aber nicht hilfreich:


    InvokeRequired wird z.B. nicht angezeigt :( - im Übrigen auch nicht im Objektbrowser.
    Das hatte ich auch schon, dass Invoke angezeigt wird, nicht aber BeginInvoke

    Macht das Sinn, die Instanziierung des Dialogs auch in einen Using-Block zu packen? Denke ja oder?

    VB.NET-Quellcode

    1. Using dlgtest As New dlgIsBusy("Test", "Test")
    2. Using New Threading.WaitDlg(dlgtest)
    3. For i = 1 To 1000
    4. Dim wait = Rnd.Next(1, 3)
    5. Threading.Thread.Sleep(wait * 500)
    6. dlgtest.UpdateDescription(i.ToString)
    7. dlgtest.UpdateTitle($"Test{i}")
    8. Next
    9. End Using
    10. End Using

    "Na, wie ist das Wetter bei dir?"
    "Caps Lock."
    "Hä?"
    "Shift ohne Ende!" :thumbsup:

    tragl schrieb:

    geht nun
    Sieh Dir meinen Snippet noch mal ganz genau an.
    Und

    RodFromGermany schrieb:

    Da der Dialog in einem anderen Thread läuft, musst Du die Ausgabe invoken.
    Dazu findest Du hier im Forum beliebig viele Beispiele.



    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:

    Sieh Dir meinen Snippet noch mal ganz genau an.


    Hatte ich ja vorher gemacht. Willst du drauf hinaus, dass du den Thread für Form2 auf Form1 startest? Das mach ich ja über ne Klasse.

    Aufruf auf der Form, die was macht:
    Spoiler anzeigen

    VB.NET-Quellcode

    1. Using dlgtest As New dlgIsBusy("Test", "Test")
    2. Using New Threading.WaitDlg(dlgtest)
    3. For i = 1 To 1000
    4. Dim wait = Rnd.Next(1, 3)
    5. Threading.Thread.Sleep(wait * 500)
    6. dlgtest.UpdateDescription(i.ToString)
    7. dlgtest.UpdateTitle($"Test{i}")
    8. Next
    9. End Using
    10. End Using


    Klasse Threading.WaitDlg
    Spoiler anzeigen

    VB.NET-Quellcode

    1. Public Class WaitDlg : Implements IDisposable
    2. Private _waitDlg As Form
    3. Public Sub New(dlg As Form)
    4. _waitDlg = dlg
    5. Dim thr As New Thread(New ThreadStart(AddressOf workerThread))
    6. With thr
    7. .IsBackground = True
    8. .SetApartmentState(ApartmentState.STA)
    9. .Start()
    10. End With
    11. End Sub
    12. Public Sub Dispose() Implements IDisposable.Dispose
    13. _waitDlg.Invoke(New MethodInvoker(AddressOf stopThread))
    14. End Sub
    15. Private Sub stopThread()
    16. _waitDlg.Close()
    17. End Sub
    18. Private Sub workerThread()
    19. With _waitDlg
    20. .TopMost = True
    21. End With
    22. Application.Run(_waitDlg)
    23. End Sub
    24. End Class


    Der dlgIsBusy
    Spoiler anzeigen

    VB.NET-Quellcode

    1. Partial Public Class dlgIsBusy : Inherits Form
    2. Public Sub New()
    3. InitializeComponent()
    4. End Sub
    5. Public Sub New(Text As String, Description As String)
    6. InitializeComponent()
    7. Me.Text = Text
    8. lblDesc.Text = Description
    9. End Sub
    10. Public Sub UpdateDescription(Description As String)
    11. If Me.InvokeRequired Then
    12. Dim act As New Action(Of String)(AddressOf UpdateDescription)
    13. Me.Invoke(act, Description)
    14. End If
    15. lblDesc.Text = Description
    16. End Sub
    17. Public Sub UpdateTitle(Title As String)
    18. If Me.InvokeRequired Then
    19. Dim act As New Action(Of String)(AddressOf UpdateTitle)
    20. Me.Invoke(act, Title)
    21. End If
    22. Me.Text = Title
    23. End Sub
    24. End Class


    Ich find das so nun auch sinnvoll gelöst, ich hatte nur Probleme mit dem Invoken - muss mich demnächst mal mehr mit der Thematik auseinandersetzen.
    Wenn du das nicht meintest, dann schreib bitte klar, was ich falsch gemacht habe ;)
    "Na, wie ist das Wetter bei dir?"
    "Caps Lock."
    "Hä?"
    "Shift ohne Ende!" :thumbsup:

    tragl schrieb:

    dann schreib bitte klar, was ich falsch gemacht habe

    VB.NET-Quellcode

    1. Public Sub UpdateTitle(Title As String)
    2. If Me.InvokeRequired Then
    3. Dim act As New Action(Of String)(AddressOf UpdateTitle)
    4. Me.Invoke(act, Title)
    5. Return ' Dieses Return hast Du vergessen
    6. End If
    7. Me.Text = Title
    8. 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!
    Hi

    Auch wenn vllt schon die Lösung gefunden wurde, hätte ich da noch einen Vorschlag. Für so einen Dialog, wo es evtl. nur eine ProgressBar, evtl. einen Button und es 3 Textfelder gibt, gibt es bereits einen Standarddialog (IProgressDialog) in Windows. Den rufe ich einfach auf und kann danach die Texte und die ProgressBar immer noch verändern. Normalerweise nimmt man diesen Dialog als FortschrittsDialog für Dateioperationen (Kopieren usw) oder man nutzt die ProgressBar im Marquee-Mode für irgendwas anderes.
    Mfg -Franky-