DrawLine dauert mir zu lange

  • VB.NET

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

    DrawLine dauert mir zu lange

    Hallo,

    vor längerer Zeit hatte ich eine Anwendung erstellt, die mir anhand der Daten aus einer DB eine Art Laufzeitdiagramm zeichnet.
    Was mich mittlerweile stört ist, dass das Zeichnen selbst zu lange dauert. Teilweise brauche ich mit allen Linien und dem drum herum ca. 6s. Ich gehe dabei aber auch 17.280 DB-Einträge, die an ein DGV gebunden sind durch.

    Wie kann ich das beschleunigen? Ich nutze nur die Methode DrawLine.

    VB.NET-Quellcode

    1. Dim tr As Graphics = Form1.CreateGraphics 'steht unter den globalen Definitionen
    2. If DGV.Rows(i).Cells(1).Value = "1" Then
    3. tr.DrawLine(gruen, i - offset, y, i - offset, yr)
    4. act += 1
    5. Else
    6. If DGV.Rows(i).Cells(3).Value = "1" And DGV.Rows(i).Cells(7).Value = "1" Then
    7. tr.DrawLine(blau, i - offset, y, i - offset, yr)
    8. sett += 1
    9. ElseIf DGV.Rows(i).Cells(3).Value = "1" And DGV.Rows(i).Cells(7).Value = "0" Then
    10. tr.DrawLine(blau, i - offset, y, i - offset, yr)
    11. sett += 1
    12. ElseIf DGV.Rows(i).Cells(3).Value = "0" And DGV.Rows(i).Cells(7).Value = "1" Then
    13. tr.DrawLine(rot, i - offset, y, i - offset, yr)
    14. stoer += 1
    15. Else
    16. tr.DrawLine(grau, i - offset, y, i - offset, yr)
    17. leer += 1
    18. End If
    19. End If


    PS: "Me.DoubleBuffered = True" habe ich bereits in der Form stehen.

    *Topic verschoben*

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „Marcus Gräfe“ ()

    Brauchst du denn alle Datenpunkte?
    Wenn nicht, könntest du den jeweiligen Mittelwert für die ältesten Daten berechnen + darstellen und nur die neusten detailliert abbilden oder dein Zeitfenster limitieren.

    Murdersquad schrieb:

    Form1.CreateGraphics
    ;( Form1 und CreateGraphics in einem Paket. Mir bluten die Augen.
    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.

    Murdersquad schrieb:

    Form1.CreateGraphics
    @VaporiZed Mir auch.
    1. Gemalt wird im Paint-Event.
    2. Dialoge: Instanziierung von Forms und Aufruf von Dialogen
    Wenn Du immer das DGV ausliest und dann noch die Werte in einen String konvertierest, dauert das.
    Lege Dir eine primäre Point-Liste an, die kannst Du einmal dem DGV als DataSource zuweisen und Du kannst sie als .PolyLine darstellen.
    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!
    @Murdersquad Poste mal ein Bild des Diagramms.
    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!
    @Murdersquad
    Ich hatte das Problem auch. Womöglich liegt es nicht an den vielen Werten, sondern einfach am Graphics-Objekt der Form.
    Ich habe die letzten Stunden in den Tipps und Tricks eine Möglichkeit gepostet.
    Zeichnen im .Paint Event wesentlich beschleunigen

    Erstelle eine Bitmap und zeichne alles statt in das Form-Graphics-Element in die Bitmap.
    Dann weise der Form die Bitmap einfach als Hintergrundbild zu.

    Du wirst sehen - geht 200x schneller ;-).
    Liebe Grüße
    Roland Berghöfer

    Meine aktuellen und kostenlos verwendbaren Tools (mit VB.NET erstellt): freeremarkabletools.com | priconman.com | SimpleCalendar | AudibleTouch | BOComponent.com | bonit.at
    Das Problem mit dem zeichnen (Paint-Event) auf sichtbaren Objekten (Form, PictureBox usw.) ist ein alter Hut. Speziell wenn sehr viel gezeichnet werden soll. Da zeichnet man am besten in einem BackBuffer, wie von dive26 vorgeschlagen, und weist dann das fertige Bitmap zb. der Form zu. Hinzu kommt das GDI+, und das wird ja von WinForms verwendet, nicht besonders schnell, im Vergleich zu anderen Möglichkeiten, ist. Ich kann Dir da nur das zeichnen mit Direct2D vorgeschlagen wo Du mit einem SwapChain arbeitest.
    Mfg -Franky-
    Könnte man das nicht in mehren Threads auslagern das laden der Datenbank? Also zb. Grade und Ungrade Zeilen in jeweilen einen Thread? Und dies dann mit dem Paint Event zusammensetzen, Nachdem die Daten mittels threading geladen wurden?

    VB.NET-Quellcode

    1. Imports System.Threading.Tasks
    2. '...
    3. Dim tr As Graphics = Form1.CreateGraphics 'steht unter den globalen Definitionen
    4. ' Iteriere über die Zeilen in DGV und erstelle eine Liste von Tasks
    5. Dim tasks As New List(Of Task)()
    6. For i As Integer = 0 To DGV.Rows.Count - 1
    7. Dim iCopy As Integer = i ' Kopie von i für die Closure
    8. Dim task As Task = Task.Factory.StartNew(Sub()
    9. Dim drawColor As Pen
    10. If DGV.Rows(iCopy).Cells(1).Value = "1" Then
    11. drawColor = gruen
    12. act += 1
    13. Else
    14. If DGV.Rows(iCopy).Cells(3).Value = "1" And DGV.Rows(iCopy).Cells(7).Value = "1" Then
    15. drawColor = blau
    16. sett += 1
    17. ElseIf DGV.Rows(iCopy).Cells(3).Value = "1" And DGV.Rows(iCopy).Cells(7).Value = "0" Then
    18. drawColor = blau
    19. sett += 1
    20. ElseIf DGV.Rows(iCopy).Cells(3).Value = "0" And DGV.Rows(iCopy).Cells(7).Value = "1" Then
    21. drawColor = rot
    22. stoer += 1
    23. Else
    24. drawColor = grau
    25. leer += 1
    26. End If
    27. End If
    28. ' Zeichne die Linie
    29. tr.DrawLine(drawColor, iCopy - offset, y, iCopy - offset, yr)
    30. End Sub)
    31. tasks.Add(task)
    32. Next
    33. ' Warte auf die Fertigstellung aller Tasks
    34. Task.WhenAll(tasks).Wait()


    So würde ich das machen: :)

    VB.NET-Quellcode

    1. Imports System.Threading.Tasks
    2. '...
    3. ' Tipp: Aktiviere das Double-Buffering für die DataGridView
    4. DataGridView1.DoubleBuffered = True
    5. '...
    6. Parallel.ForEach(DGV.Rows.Cast(Of DataGridViewRow), Sub(row)
    7. Dim i As Integer = row.Index
    8. Dim drawColor As Pen
    9. If row.Cells(1).Value = "1" Then
    10. drawColor = gruen
    11. Interlocked.Increment(act)
    12. Else
    13. If row.Cells(3).Value = "1" And row.Cells(7).Value = "1" Then
    14. drawColor = blau
    15. Interlocked.Increment(sett)
    16. ElseIf row.Cells(3).Value = "1" And row.Cells(7).Value = "0" Then
    17. drawColor = blau
    18. Interlocked.Increment(sett)
    19. ElseIf row.Cells(3).Value = "0" And row.Cells(7).Value = "1" Then
    20. drawColor = rot
    21. Interlocked.Increment(stoer)
    22. Else
    23. drawColor = grau
    24. Interlocked.Increment(leer)
    25. End If
    26. ' Tipp: Stelle sicher, dass die DataGridView nicht unnötig neu gezeichnet wird
    27. If DataGridView1.Rows(i).Visible Then
    28. ' Zeichne die Linie
    29. Using graphics As Graphics = DataGridView1.CreateGraphics()
    30. graphics.DrawLine(drawColor, i - offset, y, i - offset, yr)
    31. End Using
    32. End If
    33. End Sub)

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

    Andy2002 schrieb:

    Also zb. Grade und Ungrade Zeilen in jeweilen einen Thread?
    Dann doch lieber mit Parallel.For oder Parallel.ForEach.
    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!
    Danke euch schon mal für die zahlreichen Antworten und Vorschläge. Ich würde jetzt mal mit der Umsetzung der Tipps von dive26 und Andy2002 starten.

    Anbei mal ein Screenshot der aktuellen Grafik. Es sind zwei Grafiken, die mir die Laufzeit verschiedener Geräte darstellen. d.h. die 17.280 Datensätze teilen sich auf 2x 8.640 auf.
    Das ganze betrachte ich rollierend über die letzten 24 Stunden.

    Die beiden Anzeigen könnte ich jetzt auch nochmal in unterschiedliche Threads packen, aber bei DB-Abfragen und mehreren Threads hatte ich in der Vergangenheit meine Probleme. Ich muss mich nochmal neu in das Thema versetzen. Damals hatte ich es mit dem BackgroundWorker probiert und es gab irgendwelche Gründe, weshalb es nicht funktioniert hatte, aber die weiß ich leider nicht mehr. In meinem Kopf schwirrt noch die Aussage, dass dies mit BGW generell nicht möglich wäre.

    Anbei auch ein Bild des Diagramms.
    Bilder
    • Diagramm.jpg

      180,68 kB, 822×921, 78 mal angesehen

    Murdersquad schrieb:

    aber die weiß ich leider nicht mehr
    Auch so was gehört als Kommentar ins Programm.
    Ist es möglich, die waagerechte Struktur in ein UserControl zu packen?
    Das programmierst Du ein Mal und Du hast 24 Instanzen davon im Speicher.
    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!
    Bei dieser Art von Grafik würde ich mir die Daten per Reihe näher anschauen und gleiche nebeneinander liegende Linien durch ein größeres Rechteck ersetzen.
    Das geht dann nochmal viel, viel schneller. Und nur in den wenigen Bereichen wo sehr viele Wechsel zwischen den "Farben" sind muss jede Linie einzeln gemalt werden.
    @ErfinderDesRades
    Du hast das falsch verstanden. Im Paint-Event geschieht bei mir gar nichts. Das Paint-Event ist leer ;) .
    Der Zeichenprozess wird von extern angestoßen.

    Aber wie der Threadersteller hier ganz oben schreibt 17200 Datenbankeinträge 6 Sekunden - da passt etwas nicht.
    Ich zeichne 7200 in unter 0.5 Sekunden (das ist die Auswertung mit den 5 Graphen zu je 1440 Einzelwerten).

    @Murdersquad
    In meinem Kopf schwirrt noch die Aussage, dass dies mit BGW generell nicht möglich wäre.

    Das zeichnen in eine Bitmap geht im BGW. Dann einfach beim Complete-Event die Bitmap in die Form kopieren.
    Aber parallele Datenbankabfragen werden das Problem sein. Man müsste eventuell zuerst ALLE benötigten Daten in den Speicher einlesen und dann von dort aus mit denen arbeiten.
    Liebe Grüße
    Roland Berghöfer

    Meine aktuellen und kostenlos verwendbaren Tools (mit VB.NET erstellt): freeremarkabletools.com | priconman.com | SimpleCalendar | AudibleTouch | BOComponent.com | bonit.at

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

    ErfinderDesRades schrieb:

    "Zeichnen im .Paint Event wesentlich beschleunigen"
    ist inzwischen unter einem anderen Namen ausgelagert worden.
    @dive26 Ja, eine Demo-Lösung wäre da hilfreich.
    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!