Backgroundworker - Wie erstellt man Thread übergreifende Anweisungen

  • VB.NET

Es gibt 24 Antworten in diesem Thema. Der letzte Beitrag () ist von kwoxer.

    Backgroundworker - Wie erstellt man Thread übergreifende Anweisungen

    Also stehe vor einem etwas größeren Problem.
    Ich nutze zum aller ersten Mal das Backgroundworker Element aus der Toolbox.
    Und läuft auch ganz anständig, jedoch habe ich jetzt das Problem, dass ich innerhalb dieses Elements keine Elemente beeinflussen kann.
    Also in der DoWork vom BackGroundWorker kann man bspw. kein "ListView.Items.Remove(ListView.Items(0))" ausführen. Dabei kommt es dann zum Fehler: "Ungültiger threadübergreifender Vorgang: Der Zugriff auf das Steuerelement ListView erfolgte von einem anderen Thread als dem Thread, für den es erstellt wurde."
    Sprich man darf von dem Element nicht zu viel verlangen.
    Wie schaff ich dass Removen jetzt mit Threading Befehlen. Ich habe gehört, dass das damit möglich sein woll.
    Oder aber wie spreche ich innerhalb der Listbox andere Items außer dem ersten an.
    So jetzt seid ihr dran ;)
    das macht mein BackGroundWorker: (ganz einfache Download Routine), klappt auch und dann wenn er fertig ist wird listview ausgeführt was letztlich zum Fehler führt =/
    Listview löschen befehl klappt aber anderswo ohne Probleme!


    Private Sub BackgroundWorker_alles_DoWork(ByVal sender As System.Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker_alles.DoWork

    download = New WebClient
    download.DownloadFile(New Uri(item.SubItems(5).Text), TextBox_pfad.Text & Name_des_videos & ".mp4")
    ListView.Items.Remove(ListView.Items.Item(0))

    End Sub
    mfg
    Beispiel:

    VB.NET-Quellcode

    1. Public Class Form1
    2. Public Delegate Sub DeleteItem(ByVal index As Integer)
    3. Private Sub BackgroundWorker1_DoWork(ByVal sender As System.Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
    4. ' downloaden
    5. Me.Invoke(New DeleteItem(AddressOf DeleteListViewItem), 0)
    6. End Sub
    7. Sub DeleteListViewItem(ByVal index As Integer)
    8. ListView1.Items.RemoveAt(index)
    9. End Sub
    10. Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
    11. BackgroundWorker1.RunWorkerAsync()
    12. End Sub
    13. End Class


    Löscht das erste Item. Den Download-Code musst du vor dem invoke-Aufruf im DoWork-Sub einsetzen.
    ahh vielen vielen dank.

    klappt perfekt, genauso wie ich es mir vorgestellt hab. Und ich dachte ich muss jetzt aufwendiges Threading betreiben :)

    Sub DeleteListViewItem()
    ListView.Items.Remove(ListView.Items.Item(0))
    End Sub

    reicht aber vollkommen

    das mit dem invoke hatte ich auch schon aber ich wusste nicht wirklich was da genau für Argumente übergeben werden müssen und wie die heißen müssen.

    Kannst du mir das bitte noch erklären:

    Me.Invoke(New DeleteItem(AddressOf DeleteListViewItem), 0)

    Also DeleteItem ist klar, ist vorgegeben, dass VB weiß ich möchte etwas löschen, Addressof ist auch klar, sagen wie die Sub heißt

    aber dann ",0" wofür steht das "ParamArray args() as Object", kann mir darunter nur wenig vorstellen


    auf jede Fälle danke für deine Hilfe, ansonsten hätt ich noch ewig gebraucht :) :thumbsup:

    Edit: und jetzt noch was allerletztes:
    Wie greife ich bei einer Listview auf ein anderes Item statt dem ersten zu?
    auf das erst kommt man ja mit: "item.SubItems(5).Text"
    damit liest man den Wert der 6.Spalte aus im ersten Item

    nun will ich den Wert vom 2.item auslesen, geht das irgendwie, hab schon etliches versucht, thx schonmal, ihr seid spitze

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

    Die Invoke-Methode schaut so aus:

    Invoke (method As System.Delegate, ParamArray args() As Object) As Object

    Somit werden nach dem "," die Parameter übergeben, hier 0 als "index", um das erste Item zu löschen. Wenn du den Text eines bestimmten Items haben willst, muss das ganze so aussehen:

    VB.NET-Quellcode

    1. Public Class Form1
    2. Public Delegate Function GetItem(ByVal index As Integer)
    3. Private Sub BackgroundWorker1_DoWork(ByVal sender As System.Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
    4. ' downloaden
    5. Dim sitem As String = Me.Invoke(New GetItem(AddressOf GetListViewItem), 0) ' sitem enthält jetzt das erste item (wegen 0)
    6. MsgBox(sitem)
    7. End Sub
    8. Function GetListViewItem(ByVal index As Integer) As String
    9. Return ListView1.Items.Item(index).Text
    10. End Function
    11. Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
    12. BackgroundWorker1.RunWorkerAsync()
    13. End Sub
    14. End Class


    Edit: Code korrigiert, sorry.
    Ok, ich hoffe das ist jetzt die letzte Sache :)

    meine DoWork sieht nun folgendermaßen aus :

    VB.NET-Quellcode

    1. Private Sub BackgroundWorker1_DoWork(ByVal sender As System.Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker_alles.DoWork
    2. download = New WebClient
    3. ' On Error GoTo Err
    4. For i As Integer = 0 To ListView.Items.Count - 1
    5. Me.Invoke(New ChangeItems(AddressOf ChangeListVievItem), 0)
    6. download.DownloadFile(New Uri(Me.Invoke(New GetItem(AddressOf GetListViewItem_DL_PFAD))), _
    7. TextBox_pfad.Text & Me.Invoke(New GetItem(AddressOf GetListViewItem_DL_NAME)) & ".mp4")
    8. Me.Invoke(New ChangeItems(AddressOf DeleteListViewItem), 0)
    9. Next
    10. 'Err:
    11. ' MsgBox("Beim Download ist folgender Fehler aufgetreten : " & Err.Description, MsgBoxStyle.Critical, "Schwerwiegender Fehler")
    12. End Sub


    wie ihr bereits sieht hab ich den go to err auskommentiert weil es mit ihm Fehler gibt.

    Nun die Geschichte ist jetzt dieser kan ja an sich nicht invoked werden. Wie muss man da jetzt vorgehen?


    Weil der ist schon ziemlich wichtig um Fehler beim Downloaden abzufangen oder sonstiges was dazwischen kommen könnte.


    mfg thx 4 eure Hilfe, echt Spitzenklasse
    Ua das ist ja grausam :D

    Zunächst mal wäre es hilfreich, den Sub GetListViewItem_DL_NAME und _DL_PFAD zu posten. Aber warum benutzt du nicht einfach die DownloadFileAsync-Methode, die dir bereits die Möglichkeit, asychron zu arbeiten, bereitstellt?
    Weil dann er gleich das Item löscht, ich will aber dass der User noch sieht was heruntergeladen wird.

    Daher mache ich es ohne async ;)

    Ich weiß schon was du meinst.... fällt dir dazu vllt was ein?


    weil ich hab auch schon überlegt den DownloadFileAsync zu nehmen und den anzuweisen nur zu saugen wenn BGW.isbusy = false ist

    aber dann ist das gleiche Problem, wo soll ich ihm dann sagen wann er das Item löschen soll, denn es soll ja erst gelöscht werden wenn der Download fertigi st. Ohh man wie kompliziert ;)

    Gerne nehm ich deine Hilfe auch in Skype und über TeamViewer an. Geht vllt etwas schneller. :thumbup:


    Edit: hier die Subs und Funktionen:

    VB.NET-Quellcode

    1. Function GetListViewItem_DL_PFAD() As String
    2. Return ListView.Items.Item(0).SubItems(5).Text
    3. End Function
    4. Function GetListViewItem_DL_NAME() As String
    5. Return ListView.Items.Item(0).SubItems(0).Text
    6. End Function
    7. Sub ChangeListVievItem(ByVal index As Integer)
    8. ListView.Items.Item(index).SubItems(3).Text = "Wird heruntergeladen"
    9. End Sub
    10. Sub DeleteListViewItem(ByVal index As Integer)
    11. ListView.Items.Remove(ListView.Items.Item(index))
    12. End Sub
    Hallo :

    Wollte keinen eigenen Thread aufmachen , deswegen schreibe ich mal Hier was rein ;).

    Habe ein kleines Problem und zwar :

    Habe 2 BGW'S die gestartet werden per Button1.Diese Rechnen etwas entloses aus.Um sie wieder zu Stoppen habe ich BGW.CancelAsync genommen.SupportsCancelation steht auch auf True.Aber wenn ich diese jetzt schließen will kommt ein Fehler ind er Form :

    Dieser BackgroundWorker ist derzeit ausgelastet und kann nicht mehrere Aufgaben gleichzeitig ausführen.

    Aber warum ?.

    VB.NET-Quellcode

    1. Private Sub BackgroundWorker10_ProgressChanged(ByVal sender As System.Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker10.DoWork
    2. Dim i As Byte
    3. For i = 1 To 3
    4. i = 2
    5. Next i
    6. End Sub
    7. Private Sub BackgroundWorker11_ProgressChanged(ByVal sender As System.Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker11.DoWork
    8. Dim i As Byte
    9. For i = 1 To 3
    10. i = 2
    11. Next i
    12. End Sub


    Die berrechnen beide etwas endloses.

    Gestartet wird so :

    VB.NET-Quellcode

    1. Private Sub RadioButton4_CheckedChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles RadioButton4.CheckedChanged
    2. BackgroundWorker10.RunWorkerAsync()
    3. BackgroundWorker11.RunWorkerAsync()
    4. End Sub


    Beendet so

    VB.NET-Quellcode

    1. Private Sub RadioButton3_CheckedChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles RadioButton3.CheckedChanged
    2. BackgroundWorker10.CancelAsync
    3. BackgroundWorker11.CancelAsync
    4. End Sub


    Halt noch GAAANZ viel anderes zeug hängt aber net damit zusammen ;).
    Ok, dann bleibt es trotzdem bei "LESEN!"

    Denn: Die Endlosschleife ist ja wohl grausig! When schon, dann einfach "Do Loop". Oder besser

    VB.NET-Quellcode

    1. While Not BGW.CancellationPending
    2. System.Threading.Thread.Sleep(0)
    3. End While
    @Kwoxer: Am besten läßt du den Invoke Kram erstmal weg.

    Wenn ich das richtig sehe, machst du im BGW einen langen Download und wenn der fertig ist, willst du EIN Element aus der Liste löschen?

    Dann nimm das "WorkCompleted" Event des BGW. Dem kann man a) Parameter mitgeben und b) läuft es im Thread des aufrufenden Objekts.

    Willst du mehrere Downloads machen und dann immer ein Element aus der Liste löschen, nimm das ProgressChanged event des BGW. Es gilt selbiges wie oben.
    Ihr habt recht, geht ja ohne Backgroundworker auch viel einfacher.

    Jetzt wird folgendermaßen gesaugt:

    VB.NET-Quellcode

    1. For i As Integer = 0 To ListView.Items.Count - 1
    2. download = New WebClient
    3. download.DownloadFileAsync(New Uri(ListView.Items.Item(i).SubItems(5).Text), TextBox_pfad.Text & ListView.Items.Item(i).SubItems(0).Text & ".mp4")
    4. Next


    Klappt super, und

    VB.NET-Quellcode

    1. Private Sub download_DownloadFileCompleted(ByVal sender As Object, ByVal e As System.ComponentModel.AsyncCompletedEventArgs) Handles download.DownloadFileCompleted
    2. ListView.Items.Remove(ListView.Items.Item(0))
    3. End Sub


    klappt auch wunderbar, lediglich der kleine Schönheitsfehler, dass er immer den Oberen löscht, wenn jetzt aber das 2. item der ListView schneller zu Ende geladen ist wird nicht dieser gelöscht sondern das 1. ;)

    wie kann ich bei Completed anweisen dass zu löschen was wirklich schon fertig ist.

    klar könnte ich auch alles nach einander saugen, aber synchon ist cooler :)

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