Threadübergreifend - Backroundworker reportprogress von Klasse an Form1 Klasse übergeben

  • VB.NET
  • .NET (FX) 3.0–3.5

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

    Threadübergreifend - Backroundworker reportprogress von Klasse an Form1 Klasse übergeben

    Hallo,
    ich habe schon versucht anhand von einigen Beispielen mit events und handles die Aufgabenstellung umzusetzen, bin aber leider gescheitert. Ich hoffe ihr könnt mir dazu eine Hilfestellung geben. ?(

    Ich habe eine Funktion in eine seperate Klasse ausgelagert. Die Methode der Klasse wird aus einem BAckgroundworker in Form1 aufgerufen. Nun möchte ich gerne den reportprogress von meiner Klasse an form1 Backgroundworker übergeben um eine Progressbar mit werten zu füllen. Ohne die seperate Klasse, also als Sub in Form 1 funktioniert der Code. Ich brauche es aber zwecks wiedervernwendbarkeit in seperaten Klassen.

    Hier mal ein Beispiel

    Quellcode

    1. public class Form1
    2. Private Sub copydocs_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles copydocs.DoWork
    3. Dim copydocs As New copy
    4. copydocs.machwas
    5. End Sub
    6. Private Sub copydocs_ProgressChanged(sender As Object, e As System.ComponentModel.ProgressChangedEventArgs) Handles copydocs.ProgressChanged
    7. progressdocs.Value = e.ProgressPercentage
    8. end sub
    9. end class
    10. public class copy
    11. public sub machwas
    12. copydocs.ReportProgress(100)
    13. end sub
    14. end class


    Danke schon mal im voraus...
    @funcoder Willkommen im Forum. :thumbup:
    Zwischen 2 Formen sieht das ganze so aus:
    Spoiler anzeigen

    VB.NET-Quellcode

    1. Public Class Form1
    2. Private WithEvents dlg As Form2
    3. Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    4. If dlg Is Nothing OrElse dlg.IsDisposed Then
    5. dlg = New Form2
    6. End If
    7. If Not dlg.Visible Then
    8. dlg.Show(Me)
    9. End If
    10. Me.BackgroundWorker1.WorkerReportsProgress = True
    11. Me.BackgroundWorker1.RunWorkerAsync()
    12. End Sub
    13. Private Sub BackgroundWorker1_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
    14. For i = 1 To 100
    15. Me.BackgroundWorker1.ReportProgress(i)
    16. System.Threading.Thread.Sleep(100)
    17. Application.DoEvents()
    18. Next
    19. End Sub
    20. Private Sub BackgroundWorker1_ProgressChanged(sender As Object, e As System.ComponentModel.ProgressChangedEventArgs) Handles BackgroundWorker1.ProgressChanged
    21. Me.ProgressBar1.Value = e.ProgressPercentage
    22. If dlg IsNot Nothing AndAlso Not dlg.IsDisposed Then
    23. dlg.ProgressChanged(e.ProgressPercentage)
    24. End If
    25. End Sub
    26. End Class

    VB.NET-Quellcode

    1. Public Class Form2
    2. Public Sub ProgressChanged(value As Integer)
    3. Me.ProgressBar1.Value = value
    4. End Sub
    5. End Class
    Dass die andere Klasse auch eine Form ist, sollte Dich nicht stören, nur so siehst Du, dass und wie es funktioniert.
    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!
    Hallo
    ...und danke erstmal für deine Hilfestellung.Das geht hier im Forum ja echt unkompliziert. Da fühlt man sich ja gleich wohl :)

    Ich vergaß leider zu erwähnen wo sich die Progressbar befindet. Und das der worker über dowork ja ein objekt der Klasse generiert und das dann aufruft.

    So ähnlich hatte ich es schon versucht, aber in deinem Beispiel schient es jeweils in eine Progressbar1 in Form1 und in Form2 zu geben. Soweit ich das verstanden habe bezeiht sich ja "me." immer auf die jeweilige Klasse und in meinem Beispiel sagt mir VS2013 das die progressbar nicht vorhanden ist.

    Ich hatte es auch schon mit
    Form1.ProgressBar1.Value = value
    versucht. Hierbei wird der code als richtig angezeigt aber es kommt beim auführen folgende Exception:
    The Backgroundworker states that it doesnt report progress. Modify WorkerReportsProgress to state that it does report.

    bei me.copydocs.ReportProgress(100) und form1.copydocs.ReportProgress(100)

    Hier deine Lösung in meinem Testprogramm(in den Bold Tags sind die Änderungen, die ich aus deinem Code Übernommen hatte):

    VB.NET-Quellcode

    1. Public Class Form1
    2. [b]Private WithEvents dlg As test 'von deinem code übernommen[/b]
    3. Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    4. [b]If dlg Is Nothing OrElse dlg.IsDisposed Then 'von deinem Code, Error "isdisposed" ist kein member von windowsapp..
    5. dlg = New test
    6. End If[/b]
    7. BackgroundWorker1.RunWorkerAsync()
    8. End Sub
    9. Private Sub BackgroundWorker1_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
    10. Dim testobj As New test
    11. testobj.ProgressChanged(1)
    12. End Sub
    13. Private Sub BackgroundWorker1_ProgressChanged(sender As Object, e As System.ComponentModel.ProgressChangedEventArgs) Handles BackgroundWorker1.ProgressChanged
    14. Me.ProgressBar1.Value = e.ProgressPercentage
    15. [b]If dlg IsNot Nothing AndAlso Not dlg.IsDisposed Then 'von deinem Code übernommen, Error "isdisposed" ist kein member von windowsapp..
    16. dlg.ProgressChanged(e.ProgressPercentage)[/b]
    17. End If
    18. End Sub
    19. End Class
    20. Public Class test
    21. [b]Public Sub ProgressChanged(value As Integer)
    22. Me.ProgressBar1.value = 100
    23. End Sub[/b]
    24. End Class


    Über weitere Ideen würde ich mich freuen :)

    funcoder schrieb:

    Und das der worker über dowork ja ein objekt der Klasse generiert und das dann aufruft.
    Controls in einem anderen Thread zu generieren ist ein NoGo.
    Beschreibe bitte mal, was Dein Programm eigentlich tun soll, damit wir sehen können, ob das, was es macht, mit dem, was es tatsächlich tut, übereinstimmt.
    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,
    die Rückmeldung über reportprogress macht das ja threadübergreifend, nur eben innerhalb einer Klasse. Im rahmen von OOP lagert man aber bestimmte Dinge in andere Klassen aus um diese dann flexibler verwenden zu können.

    Meinen arbeiten Code möchte ich gerne als Klasse haben, da er mehrfach und synchron verwendet wird und natürlich als Thread um das GUI am leben zu erhalten. Eine Progressbar meldet mir den Fortschritt der Funktion in der Klasse. Das funktioniert auch als Funktion in der eigenen Klasse. Wenn ich die arbeitende Funktion nun in eine separate Klasse auslagere, dann klappt das mit der Progressbar nicht mehr, da sie kein member der Klasse ist.
    Das Beispiel hat schon mal einen eigenen eventhandler, der leider nur ohne threading funktioniert.Also ich habe bewusst den progressreport des BW nicht genutzt. Man könnte natürlich auch dahin das Event übergeben, mir ist leider aber nicht ganz klar wie.

    Mein Programm kopiert Dateien in einem Thread und im GUI soll man sehen wie weit er gerade ist.

    pseudocode:
    -starte arbeit als BW in Form1
    -erledige arbeit des BW als Objekt einer Klasse
    -gebe Progress Rückmeldung als Zahl zurück an Form1 zum visualisieren



    Hier der Code, der ohne BW schon mal funktioniert.

    VB.NET-Quellcode

    1. Public Class Form1
    2. Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    3. BackgroundWorker1.RunWorkerAsync()
    4. End Sub
    5. Sub sub_reportprogresshandler(progress As Integer)
    6. Me.ProgressBar1.Value = 100
    7. End Sub
    8. Private Sub BackgroundWorker1_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
    9. Dim testobj As New test
    10. AddHandler testobj.reportprogress, AddressOf sub_reportprogresshandler
    11. testobj.ProgressChanged()
    12.  
    13. End Sub
    14. End Class
    15. Public Class test
    16. Public Event reportprogress(ByVal progress As Integer)
    17. Public Sub ProgressChanged()
    18. 'hier wird massiv gerechnet
    19. RaiseEvent reportprogress(100)
    20. End Sub
    21. End Class


    Eine andere Lösung im Netz ware den Backgroundworker in der Klasse zu referenzieren. Hierzu fand ich bisher aber keine Dokus.
    Oder aber als Delegate mit invoke. Das muss ich mir allerdings mal in Ruhe zu Gemüte führen :S

    Dieser Beitrag wurde bereits 4 mal editiert, zuletzt von „funcoder“ ()

    funcoder schrieb:

    in der Klasse zu referenzieren.
    Zieh ihn Dir aus dem Designer auf die Form.
    Bilder
    • BGW.jpg

      10,35 kB, 328×167, 90 mal angesehen
    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,
    da ist er ja schon und funktioniert auch.
    Ich möchte ihn aber aus der anderen Klasse test ansprechen und die reportprogress funktion von dort ansprechen.
    Die Klasse Test kennt ihn aber nicht.

    Gruß

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

    funcoder schrieb:

    Ich möchte ihn aber aus der anderen Klasse test ansprechen
    Wozu?
    Ein BGW in 2 Klassen macht keinen Sinn.
    Fühe ihn dann explizit der anderen Klasse hinzu oder arbeite mit Events.
    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!
    Ich habe nur einen BGW(copydocs) in Klasse Form1.
    Progressbar(progressdocs) ist auch in Form1.
    BGWdoWork ruft Klasse copy auf.

    VB.NET-Quellcode

    1. Dim copydocsobj As New copy
    2. copydocsobj.machwas

    Wie kann ich denn aus Klasse copy auf die Progressbar zugreifen um den aktuellen Status anzuzeigen?

    Events funktionierten nicht, da raise events aus dem Thread vom BGW ausgelöst wird.(Crossthread error)
    siehe hier Threadübergreifend - Backroundworker reportprogress von Klasse an Form1 Klasse übergeben

    Hier noch mal der originalcode

    VB.NET-Quellcode

    1. public class Form1
    2. Private Sub copydocs_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles copydocs.DoWork
    3. Dim copydocsobj As New copy
    4. copydocsobj.machwas
    5. End Sub
    6. Private Sub copydocs_ProgressChanged(sender As Object, e As
    7. System.ComponentModel.ProgressChangedEventArgs) Handles
    8. copydocs.ProgressChanged
    9. progressdocs.Value = e.ProgressPercentage
    10. end sub
    11. end class
    12. public class copy
    13. public sub machwas
    14. copydocs.ReportProgress(100)
    15. end sub
    16. end class

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

    Backgroundworker sollteman heutzutage einfach in die Tonne treten. :thumbdown:

    Mittels des Await-Pattern kann man beliebige synchrone Methoden so umbauen, dass sie nebenläufig sind.
    also code dein Teil ganz normal ohne Threading, poste code, dann kann ich das glaub umbauen. Dafür brauche ich nur die Methode, die tut, was sie tun soll.

    Kannste auch selbst versuchen - gugge Async, Await und Task

    funcoder schrieb:

    Wie kann ich denn aus Klasse copy auf die Progressbar zugreifen um den aktuellen Status anzuzeigen?
    Gar nicht, denn dein Form ist ja nicht in der Klasse drin, und gehört da auch nicht rein.
    Deshalb kannste von da aus nicht auf Progressbars grabschen, die auffm Form liegen.
    Du kannst deiner Copy-Klasse ein Event verpassen, was sie sendet (so ähnlich wie der BW, aber besser). Aber das schrieb ja bereits Rod in post#8.
    Aber nicht alles auf einmal, odr?


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

    funcoder schrieb:

    aus Klasse copy auf die Progressbar zugreifen
    Die Denkweise ist falsch.

    RodFromGermany schrieb:

    arbeite mit Events.
    Wie kannst Du con der Klasse copy ein Event an die Form senden, die die Progressbar hält?
    Das ist die richtige Herangehensweise.
    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!
    Das mit dem Event habe ich ja erfolgreich implementiert...funktioniert in form1. Nur nicht als Thread.
    ...hier bleibt natürlich das GUI hängen
    Andersrum funktioniert es als thread wenn ich die Funktion der Klasse copy in Form1 als Function definiere. Funktioniert mit thread aber leider nicht objektorientiert.

    Ich werde mir mal Async, Await und Task anschauen.


    Danke
    Events

    funcoder schrieb:

    Nur nicht als Thread.
    :?:
    Was soll das heißen?

    funcoder schrieb:

    ...hier bleibt natürlich das GUI hängen
    Da machst Du offensichtlich was falsch.
    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!