GDI Zeichnen klappt nicht AUSSERHALB des Paint Events

  • VB.NET

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

    GDI Zeichnen klappt nicht AUSSERHALB des Paint Events

    Hi.

    Wie man etwas auf die Form zeichnet weiss ich:

    VB.NET-Quellcode

    1. Dim playerSprite As New Bitmap(My.Resources.Unten)
    2. g.DrawImage(playerSprite, XPOS, YPOS, 24, 49)


    Nun wollte ich dieses mal via SUB machen:

    VB.NET-Quellcode

    1. Public Sub DrawObject(ByVal Pfad As String, ByVal X As Integer, ByVal Y As Integer, ByVal xg As Integer, ByVal yg As Integer)
    2. Dim playerSprite As New Bitmap(Pfad)
    3. Dim g As Graphics = Graphics.FromImage(playerSprite)
    4. g.DrawImage(playerSprite, X, Y, Xg, Yg)
    5. End Sub


    Doch rufe ich diese so auf:

    VB.NET-Quellcode

    1. DrawObject("Player.png", "80", "104", "334", "255")
    Zeichnet er garnichts auf die Form.

    Warum?

    Danke.

    Mfg. TGS

    TheGameSiders schrieb:

    Wie man etwas auf die Form zeichnet weiss ich
    Offensichtlich nicht.
    Das geht nämlich so:

    VB.NET-Quellcode

    1. Private Sub Form1_Paint(sender As System.Object, e As System.Windows.Forms.PaintEventArgs) Handles MyBase.Paint
    2. e.Graphics.DrawRectangle(Pens.Black, New Rectangle(10, 10, 100, 100))
    3. End Sub
    Es gibt auch ein e.Graphics.DrawImage() mit 30 Überladungen, da musst Du Dir das richtige raussuchen
    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!
    Artentus hat revht. Zeichne im Paint Event und pfusche nicht mit Bitmaps oder Creategraphics rum. Wenn du dir jetzt denkst aber wie soll ich das mit den Events dann machen (Zeichnen bei Event) sag ich dir das du Invalidate verwenden kannst ;)

    8-) faxe1008 8-)
    Das Problem mit dem Zeichnen außerhalb eines Paint Events kann ich verstehen.
    Wenn du zb in dem Paint Event viel (sehr viel) zu zeichnen hast, ist es oft nicht so gut, nur wegen einer Kleinigkeit, Alles mit invalidate neu zu zeichnen.
    Mit freundlichen Grüßen

    Linux
    Es ist aber leider die einzig akzeptable Möglichkeit. Dinge, die außerhalb der Paint-Events gezeichnet werden, sind nicht konsistent, d.h. sie verschwinden ganz schnell wieder. Außerdem ist das Erstellen eines Graphics-Objektes auch nicht ganz unperformant und DoubleBuffer gibt es z.B. nicht.
    Wenn man wirklich so viel zu zeichnen hat, dass es laggt, dann sollte man nicht GDI+ verwenden, sondern für Spiele DirectX und für GUIs WPF.

    VB.NET-Quellcode

    1. DrawObject("Player.png", "80", "104", "334", "255")

    Ich hoffe das ist ein schlechter Witz/vertipper. Du übergibst als Parameter nicht wirklich Strings?! Falls doch, dann:
    Option Strict On !!!!

    Und: klar zeichnet er nichts auf die Form, es zeichnet ja auf die Bitmap. Wenn du auf die Form zeichnen willst arbeite im Paint-Event. Zeichnest du auf eine Bitmap, dann zeichnest du eben auf die Bitmap aber nirgends anders hin.

    @Linux13524
    Tja, das läuft dann darauf hinaus, dass du dein Paint-Event bzw die darunter liegende Klassenstruktur nicht anständig gecoded hast. .invalidate() hat nämlich Überladungen wo du den neuzuzeichnenden Bereich übergeben kannst. Das bringt schon etwas mehr Performance. Wenn du es 100%ig machen willst kannst du deine Objekte ganz gezielt neuzeichnen: einmal den alten Bereich des Objekts und einmal den Neuen. So wird nichts zuviel gezeichnet und lässt sich mit GraphicsPaths machen. Suche mal nach 'gezieltem OwnerDrawing' vom ErfinderDesRades, der hat das besser erklärt als ich. In meinem GDI-Tutorial ist das+Links auch zu finden^^
    lg
    @Artentus
    Stimmt. So ein Problem, dass die Sachen irgendwie verschwinden hatte ich auch mal. Daraufhin hab ich dann doch wieder alles in das Paint Event. :D

    FreakJNS schrieb:

    .invalidate() hat nämlich Überladungen
    Mist!
    Warum hat mir das nie jemand gesagt :whistling:
    Ich werd mir das mal genau anschauen.
    Mit freundlichen Grüßen

    Linux
    @TheGameSiders: Ist Dein Problem inzwischen gelöst?
    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!
    Da wüsste ich zu gerne warum das unmöglich sein soll. Wenn du etwas auf die Form zeichnen willst geht das Paint-Event IMMER (oder bring mir ein Gegenbeispiel). Das du im Paint-Event arbeiten musst heißt btw nicht, dass du keine anderen Methoden aufrufen darfst. Wichtig ist, dass du nur auf das Graphics-Objekt vom Paint-Event zeichnest (=> e.Graphics), dieses also ggf als Methodenparameter weiterreichst.
    Also unmöglich ist es nicht. Du sollst es halt einfach nicht machen. Der Code wäre so:

    VB.NET-Quellcode

    1. Public Sub DrawObject(ByVal Pfad As String, ByVal X As Integer, ByVal Y As Integer, ByVal xg As Integer, ByVal yg As Integer)
    2. Dim playerSprite As New Bitmap(Pfad)
    3. Dim g As Graphics = Graphics.FromHwnd(Me.Handle) 'Hier muss du das handle von dem Objekt, auf dem du zeichnen willst, angeben
    4. g.DrawImage(playerSprite, X, Y, Xg, Yg)
    5. End Sub


    Aber wie FreakJNS schon sagte: Nimm das Graphic-Objekt vom Paint-Event.
    Mit freundlichen Grüßen

    Linux
    @Linux13524
    FromHwnd ist afaik genau das gleiche wie .createGraphics, nur, dass du auf einen beliebigen Fenster-Handle zeichnen kannst. Ich wüsste nicht, warum man überhaupt mit .createGraphics zeichnen sollte (.fromHwnd schon, aber nicht, wenn man es wie creategraphics benutzt).

    Also einfach das GraphicsObjekt aus dem Paint-Event nehmen und weiterreichen.
    Im Paintevent zu zeichnen ist keine Erfindung um dich zu ärgern sondern um zu helfen. Das System sendet eine Paint Nachricht an dein Fenster, wenn es neu gezeichnet werden muss. Dann wird sich der DeviceContext des Fensters gehohlt, damit ein Graphics-Objekt erzeugt und das Paint-Event gefeuert. Jetzt kannste einfach drauf loszeichnen. Beim Doublebuffering wird zuerst ne kompatible Bitmap erzeuigt und auf dessen DC gemalt anschließend wird der DC auf den DC der Form kopiert, so wird das Flackern vermieden. Nimmste jetzt Creategraphics wird der DC gehohlt und draufgemalt, da wird von Haus aus kein DoubleBuffer unterstützt. Der Nachteil ist entweder löscht das System das Gezeichnete, oder es ist absolut unperformant weils immer neu gezeichnet wird.
    @Linux13524
    Das klappt nun auf meinem Steuerelement wenn ich es so aufrufe:

    VB.NET-Quellcode

    1. DrawObject("Test.png", 208, 236, 50, 50)

    Rufe ich es aber von einer anderen Form so auf:

    VB.NET-Quellcode

    1. DLL.DrawObject("Test.png", 208, 236, 50, 50)




    Was ist daran wieder falsch?..

    Danke :)

    Mfg. TGS



    EDIT: Hab vergessen das Bild in dem Ordner zu speichern.
    So, nun kein Fehler, aber wieder erscheint nichts auf der Form sondern auf dem Steuerelement..
    ^ this

    mal davon abgesehen, dass man IN der Zeichenroutine nur zeichnen soll. Das Laden von Bildern gehört da definitiv nicht mit rein, dass muss vorher geschehen, weil es Zeitaufwändig ist. Ständige Plattenzugriffe sind langsam, und genau das tust du: bei jedem Neuzeichnen die komplette Grafik neu ladne. Wer sowas programmiert braucht garnicht mit Aussagen wie "GDI ist langsam" kommen.

    Um es deutlich zu sagen: Der Code von Linux13524 ist Dreck! (ist nicht böse gemeint, wenn man z.B. auf fremde Programm-Fenster zeichnen wollte müsste man das so machen, aber hier NICHT).

    Außerdem glaube ich so langsam, dass @TheGameSiders die Grundlagen der Programmierung von .NET mal wiederholen sollte. Ich habe das Gefühl, dass du da irgendwelche Aufrufe kreuz und quer machst ohne zu wissen wo vorne und hinten ist. Aber nochmal: Du willst etwas auf Form1 zeichnen, dann arbeite im Form1-Paint-Event. Du willst etwas auch Panel24 zeichnen, dann arbeite im Panel24-Paint-Event. Von dort kannst du dann hurr.durr.drawObject(e.graphics,eineBitmap,...) aufrufen, du siehst: Das Graphics-Objekt aus dem Paint-Event wird der Methode als Parameter übergeben, da ist kein CreateGraphics/FromHwnd/Blödsinn notwendig.

    Weiterhin glaube ich, dass dir ein passendes Klassenkonzept fehlt. Methoden mit gefühlten siebzig Parametern sollte es nicht geben, denn ein OOP Konzept hätte eine Klasse ZeichenbaresObjekt, dass seine Position/Grafik/etc kennt und eine Methode .draw(g as graphics) wo es sich selbst auf das Graphics-Objekt zeichnet.

    lg
    Also als "Dreck" würde ich meinen Code nicht bezeichnen. Er ist halt nicht die Lösung des hier angesprochenen Falls, ist aber dennoch die Antwort auf die Frage des TEs.

    @TheGameSiders
    Bei .FromHwnd musst du das Handle des Objekts auf dem du zeichnen willst angeben.
    Und deshalb ist es dann vorteilhaft, wenn du auf fremde Fenster zeichen willst (wie FreakJNS ja auch schon gesagt hat), aber nicht in deinem Fall.
    Mit freundlichen Grüßen

    Linux

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „Linux13524“ () aus folgendem Grund: Schreibfehler