Performance Problem bei Aktualisieren einer ListView

  • VB.NET

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

    Performance Problem bei Aktualisieren einer ListView

    Hallo zusammen,

    ich habe folgendes Problem beim aktualisieren einer ListView, nämlich dass der Code bis zu 13% meines
    Prozessors beansprucht, außerdem läuft das Programm nicht mehr wirklich gut.
    Ich habe eine ListView mit fogenden Columns: Name ID Status
    In dieser ListView werden sämtliche laufende Prozesse des Systems + ID und Status aufgelistet.
    Aktualisieren tu ich die ListView folgendermaßen:

    Code

    Visual Basic-Quellcode

    1. Dim schonVorhanden As Boolean = False
    2. Dim nichtMehrVorhanden As Boolean = True
    3. For Each Prozess As Process In Process.GetProcesses
    4. schonVorhanden = False
    5. For Each idItem As ListViewItem In LbProzesse.Items
    6. If idItem.SubItems(1).Text = Prozess.Id Then
    7. schonVorhanden = True
    8. End If
    9. If Prozess.Responding = True And Not idItem.SubItems(2).Text = "Wird ausgeführt" Then
    10. idItem.SubItems(2).Text = "Wird ausgeführt"
    11. ElseIf Prozess.Responding = False And Not idItem.SubItems(2).Text = "Keine Rückmeldung" Then
    12. idItem.SubItems(2).Text = "Keine Rückmeldung"
    13. End If
    14. Next
    15. If schonVorhanden = False Then
    16. With LbProzesse.Items.Add(Prozess.ProcessName)
    17. .SubItems.Add(Prozess.Id)
    18. If Prozess.Responding = True Then
    19. .SubItems.Add("Wird ausgeführt")
    20. Else
    21. .SubItems.Add("Keine Rückmeldung")
    22. End If
    23. End With
    24. End If
    25. Next
    26. For Each item As ListViewItem In LbProzesse.Items
    27. nichtMehrVorhanden = True
    28. For Each Prozess As Process In Process.GetProcesses
    29. If Prozess.Id = item.SubItems(1).Text Then
    30. nichtMehrVorhanden = False
    31. End If
    32. Next
    33. If nichtMehrVorhanden = True Then
    34. Me.Invoke(Sub()
    35. item.Remove()
    36. End Sub)
    37. End If
    38. Next



    Kennt jemand eine bessere Methode eine ListView zu aktualisieren (inkl. ob der Prozess noch ausgeführt wird)
    ohne die ListView ständig zu clearen und neu zu befüllen (da die aktualisierung alle 0,5 sek mittels eines Timers ausgeführt wird)

    Danke schon mal für alle Antworten
    @Kilian_98 Nimm ein DataGridView und eine entsperechende DataTable.
    Ein Beispiel mit Double findest Du hier, musst nur DeineDatentypen reinschreiben.
    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!
    Afaik könntest du mit ListView.BeginUpdate und ListView.EndUpdate arbeiten.

    Dann wird das ganze erst neugezeichnet, nachdem alle Items reingepackt bzw. aktualisiert wurden
    und das ganze sollte wesentlich schneller gehen und weniger Leistung fressen.

    Kilian_98 schrieb:

    wie aktualisier ich das dann ?
    Mach mal ganz unvoreingenommen das Beispiel aus meinem Post #3.
    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!
    @SplittyDev Gute Idee, werde es mal testen

    @RodFromGermany hatte mich noch nie damit damit beschäftigt, und als ichs mir auf die Form gezogen hab,
    sahs für mich aus wie eine Tabelle, die ich eigentlich nicht brauche. Hab mir nun ein paar Videos angeschaut, und werde
    es mal ausprobieren

    Danke für alle bisherigen Antworten

    EDIT: Mit ListView.BeginUpdate und ListView.EndUpdate bleibt die Performance gleich, es verbessert sich leider nichts.

    Aber irgendwie muss es doch gehen, andere Programme schaffens ja auch


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

    Vielleicht wendest du BeginUpdate und EndUpdate falsch an..
    Das sollte ungefähr so aussehen:

    Spoiler anzeigen

    Visual Basic-Quellcode

    1. Dim schonVorhanden As Boolean = False
    2. Dim nichtMehrVorhanden As Boolean = True
    3. ListView.BeginUpdate () ' Vor allen aktionen aufrufen
    4. For Each Prozess As Process In Process.GetProcesses
    5. schonVorhanden = False
    6. For Each idItem As ListViewItem In LbProzesse.Items
    7. If idItem.SubItems(1).Text = Prozess.Id Then
    8. schonVorhanden = True
    9. End If
    10. If Prozess.Responding = True And Not idItem.SubItems(2).Text = "Wird ausgeführt" Then
    11. idItem.SubItems(2).Text = "Wird ausgeführt"
    12. ElseIf Prozess.Responding = False And Not idItem.SubItems(2).Text = "Keine Rückmeldung" Then
    13. idItem.SubItems(2).Text = "Keine Rückmeldung"
    14. End If
    15. Next
    16. If schonVorhanden = False Then
    17. With LbProzesse.Items.Add(Prozess.ProcessName)
    18. .SubItems.Add(Prozess.Id)
    19. If Prozess.Responding = True Then
    20. .SubItems.Add("Wird ausgeführt")
    21. Else
    22. .SubItems.Add("Keine Rückmeldung")
    23. End If
    24. End With
    25. End If
    26. Next
    27. For Each item As ListViewItem In LbProzesse.Items
    28. nichtMehrVorhanden = True
    29. For Each Prozess As Process In Process.GetProcesses
    30. If Prozess.Id = item.SubItems(1).Text Then
    31. nichtMehrVorhanden = False
    32. End If
    33. Next
    34. If nichtMehrVorhanden = True Then
    35. Me.Invoke(Sub()
    36. item.Remove()
    37. End Sub)
    38. End If
    39. Next
    40. ListView.EndUpdate () ' Nach allen aktionen aufrufen
    Nein, am Anfang hab ichs tatsächlich falsch gemacht, aber Visual Basic hat mir schnell gesagt, wie es richtig ist, trotzdem danke für die Antwort.

    @Rod: Da müsste ich mich erst richtig einarbeiten, außerdem ist doch das DataGridView eher ne Tabelle (oder hab ich da ein paar Einstellungen über sehen, sodass sich das einarbeiten lohnen würde?)
    Wäre das geeignet für einen Taskmanager?

    Gibt es da nicht noch ne andere Möglichkeit, mit weniger Schleifen (das Programm muss schließlich ca 2000 Abfragen in nicht mal einer halben Sekunde ausführen). Kennt jemand noch ne Methode ohne so viele Schleifen ?
    Gibt es schon eine eingebaute?
    Bin für jeden Tipp/Link dankbar (bei Google kam nichts gescheitert heraus)
    @Kilian_98 Machma Option Strict On und poste ein C&P-Snippet, das Deinen effekt reproduziert.
    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!
    Code hatte ich doch schon gepostet ? Oder was meinst du mit 'C&P-Snippet, das Deinen effekt reproduziert.' ?
    Hier jedenfalls der Code noch mal mit Option Strict On:
    Code fürs Aktualisieren

    VB.NET-Quellcode

    1. Option Strict On
    2. LbProzesse.BeginUpdate()
    3. Dim schonVorhanden As Boolean = False
    4. Dim nichtMehrVorhanden As Boolean = True
    5. For Each Prozess As Process In Process.GetProcesses
    6. schonVorhanden = False
    7. For Each idItem As ListViewItem In LbProzesse.Items
    8. If idItem.SubItems(1).Text = cStr(Prozess.Id) Then
    9. schonVorhanden = True
    10. End If
    11. If Prozess.Responding = True And Not idItem.SubItems(2).Text = "Wird ausgeführt" Then
    12. idItem.SubItems(2).Text = "Wird ausgeführt"
    13. ElseIf Prozess.Responding = False And Not idItem.SubItems(2).Text = "Keine Rückmeldung" Then
    14. idItem.SubItems(2).Text = "Keine Rückmeldung"
    15. End If
    16. Next
    17. If schonVorhanden = False Then
    18. With LbProzesse.Items.Add(Prozess.ProcessName)
    19. .SubItems.Add(CStr(Prozess.Id))
    20. If Prozess.Responding = True Then
    21. .SubItems.Add("Wird ausgeführt")
    22. Else
    23. .SubItems.Add("Keine Rückmeldung")
    24. End If
    25. End With
    26. End If
    27. Next
    28. For Each item As ListViewItem In LbProzesse.Items
    29. nichtMehrVorhanden = True
    30. For Each Prozess As Process In Process.GetProcesses
    31. If cStr(Prozess.Id) = item.SubItems(1).Text Then
    32. nichtMehrVorhanden = False
    33. End If
    34. Next
    35. If nichtMehrVorhanden = True Then
    36. Me.Invoke(Sub()
    37. item.Remove()
    38. End Sub)
    39. End If
    40. Next
    41. LbProzesse.EndUpdate()




    EDIT: Habs nun hinbekommen. Nun hab ich eine CPU Auslastung von 1-2%. Ich denke das geht noch ;)
    Für alle die ein ähnliches Problem haben, hab ich hier mal meinen Code reingeschrieben:

    besser funktionierender Code

    VB.NET-Quellcode

    1. Option Strict On
    2. Dim ListView2 As New ListView
    3. ListView2.Columns.Add("Name")
    4. ListView2.Columns.Add("ID")
    5. Dim schonDa As Boolean = False
    6. 'füllt die "Virtuelle" ListView2 mit den aktuellen Prozessen
    7. ListView2.Items.Clear()
    8. For Each prozess As Process In Process.GetProcesses
    9. With ListView2.Items.Add(prozess.ProcessName)
    10. .SubItems.Add(CStr(prozess.Id))
    11. End With
    12. Next
    13. gehezu:
    14. For Each item As ListViewItem In ListView2.Items 'geht alle listview items durch und vergleicht sie mit der anderen listview
    15. If Not item.SubItems(1).Text = ListView1.Items(item.Index).SubItems(1).Text Then
    16. Dim index As Integer = item.Index
    17. 'schaut nach, ob der prozess an anderer Stelle schon vorhanden ist (wenn sich ein nicht mehr laufender prozess dazwischendrängt) hier: sucht in den nächsten sechs Prozessen und nicht in allen, da es sehr unwahrscheinlich ist, dass mehr als sechs Prozesse gleichzeitig beendet werden
    18. For a = 1 To 6
    19. index += 1
    20. Try
    21. If item.SubItems(1).Text = ListView1.Items(index).SubItems(1).Text Then
    22. schonDa = True
    23. End If
    24. Catch ex As Exception
    25. GoTo gehezu
    26. End Try
    27. Next
    28. If schonDa = True Then 'wenn sich ein anderer Prozess dazwischengedrängt hat, wird dieser entfernt
    29. ListView1.Items.RemoveAt(item.Index)
    30. GoTo gehezu 'da sich eine listview verändert hat, wird noch mal von vorne durchgegangen
    31. Else 'ist der Prozess noch nicht vorhanden, wird er an der selben Stelle wie in der "virtuellen" listview hinzugefügt
    32. With ListView1.Items.Insert(item.Index, ListView2.Items(item.Index).Text)
    33. .SubItems.Add(ListView2.Items(item.Index).SubItems(1).Text)
    34. End With
    35. GoTo gehezu 'da sich eine listview verändert hat, wird noch mal von vorne durchgegangen
    36. End If
    37. End If
    38. Next
    39. 'Die Listbox ist Aktualisiert, ohne Performance Probleme (bis zu 2% CPU Last) und ohne dass Markierung oder Position der Scrollbar verlorengeht




    Danke nochmal für alle Antworten

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

    Kilian_98 schrieb:

    'C&P-Snippet, das Deinen effekt reproduziert.'
    Ich kopiere Deinen Code in mein Projekt und sehe den von Dir beschriebenen Effekt.
    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!