GDI flüssig neuzeichnen

  • VB.NET

Es gibt 14 Antworten in diesem Thema. Der letzte Beitrag () ist von ErfinderDesRades.

    GDI flüssig neuzeichnen

    Hi,

    Ich verwende GDI um etwas auf eine Picturebox zu zeichnen, diese Funktion wird zirka jede Sekunde erneut aufgerufen.
    Damit ich meine Form deshalb nicht vollknalle wird das vorher gezeichnete gelöscht (habe per Suche rausgefunden, man solle es mit Form1.Invalidate() machen).

    Es funktioniert auch, das einzige Problem ist das zwischen dem Löschen und dem Neuzeichnen zirka 1 Sekunde liegt, was natürlich das ganze für den Benutzer sichtbar machen lässt.
    Ich hätte gerne das das gezeichnete einfach "wandert" als wäre es statt neuzeichnen einfach verschoben bzw. von A nach B gesetzt (unmittelbar).

    Meine Funktion sieht so aus:

    VB.NET-Quellcode

    1. Sub drawtest(ByVal xpos As Point, ByVal ypos As Point)
    2. Form1.Invalidate()
    3. Dim b As New System.Drawing.SolidBrush(System.Drawing.Color.White)
    4. Dim g As Graphics = Form1.PictureBox1.CreateGraphics()
    5. g.FillRectangle(b, New Rectangle(xpos, ypos, 50, 50))
    6. g.Dispose()
    7. b.Dispose()
    8. End Sub



    //edit: Aus irgendeinem Grund verhaut mir die VB-Funktion des Forums die Formatierung. Danke ErfinderDesRades

    Wulf schrieb:

    Schau dir doch mal die Überladungen von Invalidate() an da kannst du auch ein Rechteck übergeben. Dadurch wird dann nur dieser Bereich neu gezeichnet.


    Ich werde das nachher ausprobieren. Ist es denn ein Unterschied ob ich nur auf den Bereich oder die Form Invalidate() ausführe? Denn selbst bei einem Rechteck lag zwischen dem Aktualisieren eine Sekunde.
    Das Problem wäre nur den alten Bereich des Rechteckes (es werden später mehrere sein) rauszufinden.
    niemals Control.CreateGraphics() verwenden!

    Immer nur im Paint-Event malen, mit den dort bereitgestellten eventArgs.

    gugge Control mit beweglicher Figur
    Allerdings ist ein Laufband besonders unperformant, weil für jedes Vorrücken die gesamte Bildfläche neu zu zeichnen ist.

    GDI ist für Animationen nur bedingt tauglich.

    Form.Invalidate() dürfte keinen Sinn ergeben, wenn du garnet ins Form malst, sondern in die PB.
    Das mit Paint funktioniert wesentlich schneller, leider flackert es tierisch (kein Wunder, es wird ja auch ständig gelöscht und neugezeichnet).
    Hab Form1.Invalidate() in Form1.Picturebox1.Invalidate() geändert, hat den selben Effekt wie vorher.

    Wie könnte man denn mein Vorhaben anders realisieren? Was ich möchte ist quasi, dass ich jede Sekunde ein neues "Bild" anzeige und zwar unmittelbar.
    Rein theoretisch sollte es ja möglich sein, jetzt ist nur die Frage wie man es ohne Flackern und ohne das das Bild für eine Sekunde ganz verschwindet, bevor das neue angezeigt wird.

    ErfinderDesRades schrieb:

    dassichdir einen Link mitgeteilt habe ist dir entgangen?


    Nein das nicht, aber ich weiß leider nicht wie ich ihm extern sagen kann was er wo zeichnen soll. Im Code wird ja ein Pfeil hinzugefügt und dieser dann beliebig gedreht bzw. verändert.
    Ich möchte ja extern sagen "Lösche alle Punkte und füge den, den, den Punkt hinzu".
    Ist mein erster Versuch mit GDI, vielleicht hätte ich es ins Anfängerforum schreiben sollen.

    @brichun: Hatte ich schon gemacht, leider liegt dazwischen immer noch eine Pause.

    Hatschi schrieb:

    Ist mein erster Versuch mit GDI, vielleicht hätte ich es ins Anfängerforum schreiben sollen.

    Mach Dir doch mal ein kleines Testprogramm mit 4 Button und spiele damit ein wenig rum:
    Spoiler anzeigen

    VB.NET-Quellcode

    1. Public Class Form1
    2. Private kennung As Integer = 0
    3. Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
    4. kennung = 0
    5. Invalidate()
    6. End Sub
    7. Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
    8. kennung = 1
    9. Invalidate()
    10. End Sub
    11. Private Sub Button3_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button3.Click
    12. kennung = 2
    13. Invalidate()
    14. End Sub
    15. Private Sub Button4_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button4.Click
    16. kennung = 3
    17. Invalidate()
    18. End Sub
    19. Private Sub Form1_Paint(ByVal sender As System.Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles MyBase.Paint
    20. Dim g As Graphics = e.Graphics
    21. Dim ft As New Font("Arial", 12)
    22. Dim pt As New Point(60, 60)
    23. Select Case kennung
    24. Case 0 : g.DrawString("bla", ft, Brushes.Black, pt)
    25. Case 1 : g.DrawLine(Pens.Red, pt, New Point(100, 100))
    26. Case 2 : g.DrawEllipse(Pens.Green, New Rectangle(20, 20, 120, 120))
    27. Case 3 : g.DrawImage(New Bitmap("C:\Temp\bla.bmp"), pt)
    28. End Select
    29. End Sub
    30. 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!
    Mir bereitet es Kopfzerbrechen das ich nicht weiß ich wie extern die Koordinaten bestimmen kann.
    Ich kann ja unmöglich um die 200 Select Case Zeilen schreiben.

    Spontan fällt mir da ein zwei dimensionaler Array ein, aber den zu füllen bereitet mir wiederum Kopfzerbrechen, da ich ja nicht wie bei einer Listbox einfach "add" schreiben kann, sondern die Position bestimmen muss.

    Ich bräuchte also so etwas wie der PseudoCode hier:

    Form1.Paint

    For each eintrag in ?
    g.DrawRectangle(Pens.Black, eintrag)
    Next

    Wie kann ich das realisieren?
    nimm doch das DrawObject aus dem gegebenen Link, von dem du sagst, er sei dir nicht entgangen.

    Davon eine List(Of DrawObject), und dein Pseudocode lautet:

    VB.NET-Quellcode

    1. For each eintrag in myDrawObjectListe
    2. eintrag.Draw(e.Graphics)
    3. Next


    DrawObject ist ja so designed, dass du im TemplatePath eine beliebige Figur eingeben kannst, und über .Scale legst du die Größe fest.
    .Location kennsteja bereits, also noch fragen?
    Ich denke deine Frage war hypthetisch gemeint, dennoch: Ja habe ich.


    Ich habe das mit dem DrawObject nämlich nicht ganz verstanden.
    Also mein Code:

    VB.NET-Quellcode

    1. Dim punkte As New List(Of DrawObject)
    2. ' Code der im Timer steht
    3. Dim abc As New DrawObject(PictureBox1)
    4. abc.Location = New Point(xpos, ypos)
    5. abc.Scale = New Size(5, 5)
    6. abc.TemplatePath.AddRectangle(New Rectangle(New Point(xpos, ypos), New Size(5, 5)))
    7. abc.ApplyChanges()
    8. punkte.Add(abc)
    9. ' Paint event
    10. Private Sub Form1_Paint(ByVal sender As System.Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles PictureBox1.Paint
    11. For Each eintrag In punkte
    12. eintrag.Draw(e.Graphics)
    13. Next
    14. End Sub


    Jetzt wird gar nichts gezeichnet, vermutlich weil die Liste "Punkte" leer ist, oder weil TemplatePath falsch ist. Zugegebenermaßen ich habe keine Ahnung.
    sieht mir garnemalso schlecht aus!

    Nur sollteste den DokuKommentar beachten:

    VB.NET-Quellcode

    1. ''' <summary>
    2. ''' Zur Aufnahme des Musters der Figur. Verwenden Sie Größenangaben im Bereich von +-1.0,
    3. ''' und legen Sie mit Public Property Scale() die Größe der Darstellung fest.
    4. ''' </summary>
    5. Public ReadOnly Property TemplatePath() As GraphicsPath
    6. Get
    7. Return _TemplatePath
    8. End Get
    9. End Property


    (Den anfang findich immer am schwierigsten, wenn erstmal was zu sehen ist, kann mans relativ leicht zurechtrücken)

    Vlt richteste dich noch stärker anne vorlage, also erst den T-Path, dann scalieren, und .ApplyChanges() nicht vergessen.

    Und vlt nicht gleich innem Timer, sondern erstmal innem button_Click probieren