GDI / neu hinzugekommenen Bereich zeichnen

  • C#
  • .NET (FX) 4.5–4.8

Es gibt 43 Antworten in diesem Thema. Der letzte Beitrag () ist von mrMo.

    GDI / neu hinzugekommenen Bereich zeichnen

    Tach zusammen,

    ​seit einigen Tagen widme ich meine Aufmerksam dem Thema "Zeichnen mit GDI". ​Hierfür habe ich mir ein kleines Testprojekt "GlossyKnopf" ausgedacht.
    Hier zeichne ich in einem UserControl (erbt von Button) ein Knopf. Soweit so gut. Das Zeichnen an sich ist auch nicht das Problem, mag sein das noch nicht optimal, aber das soll hier erstmal nicht das Thema sein.


    ​Ziehe ich die Form auf der sich der Knopf befindet zu und wieder auf, macht der Knopf lustige Sachen:

    Vermutung des Problems: Der Knopf wird wohl immer wieder neu gezeichnet, wenn er "sichtbar" wird, weshalb man wohl nur den neu hinzugekommenen Bereich zeichnen sollte(?).

    ​Nun habe ich mal die Suchmaschine angeworfen und bin vermutlich an die Wurzel des Übel gestoßen. Hab zwar wunderbare Tutorials zum Thema gefunden, jedoch waren die meist viel zu überladen.

    ​Evtl. mag mir jemand auf die Sprünge helfen?

    ​Hier der Code aus dem Paint Event des Knopfs:
    Spoiler anzeigen

    C#-Quellcode

    1. private void Zeichnen(object sender, PaintEventArgs e)
    2. {
    3. Rectangle rect = e.ClipRectangle;
    4. e.Graphics.SmoothingMode = SmoothingMode.HighQuality;
    5. e.Graphics.CompositingQuality = CompositingQuality.HighQuality;
    6. e.Graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit;
    7. LinearGradientBrush br;
    8. if (!MausIstDrauf && !MausKlickt)
    9. {
    10. br = new LinearGradientBrush(rect, _MouseOverFarbe1, _MouseOverFarbe2, 90, true);
    11. }
    12. else if (MausIstDrauf && !MausKlickt)
    13. {
    14. br = new LinearGradientBrush(rect, _MouseDownFarbe1, _MouseDownFarbe2, 90, true);
    15. }
    16. else
    17. {
    18. br = new LinearGradientBrush(rect, _MouseClickFarbe1, _MouseClickFarbe2, 90, true);
    19. }
    20. e.Graphics.FillRectangle(br, rect);
    21. Rectangle r = rect;
    22. r.Width -= 1;
    23. r.Height -= 1;
    24. e.Graphics.DrawRectangle(new Pen(new SolidBrush(_ButtonBorderColor)), r);
    25. StringFormat stringFormat = new StringFormat();
    26. stringFormat.Alignment = StringAlignment.Center;
    27. stringFormat.LineAlignment = StringAlignment.Center;
    28. Font f = new Font("Arial", 8, FontStyle.Regular, GraphicsUnit.Point);
    29. if (_Text != "")
    30. e.Graphics.DrawString(_Text, f, new SolidBrush(Color.Black), rect, stringFormat);
    31. }


    ​Das ganze Projekt hab ich auch mal angehängt. Falls Bedarf besteht.
    Dateien
    "Gib einem Mann einen Fisch und du ernährst ihn für einen Tag. Lehre einen Mann zu fischen und du ernährst ihn für sein Leben."

    Wie debugge ich richtig? => Debuggen, Fehler finden und beseitigen
    Wie man VisualStudio nutzt? => VisualStudio richtig nutzen
    Also ich habe es getestet und ich habe das Problem nicht ...
    (Windows 7 64bit - Ultimate, VS 2013 Ultimate)

    Abgesehen davon solltest du noch bisschen an der Zeichenmethode arbeiten, besonders im Bezug auf disposen etc :)
    Mfg: Gather
    Private Nachrichten bezüglich VB-Fragen werden Ignoriert!


    Gather schrieb:

    Also ich habe es getestet und ich habe das Problem nicht ...
    (Windows 7 64bit - Ultimate, VS 2013 Ultimate)
    Mit meinem Testprojekt???

    Abgesehen davon solltest du noch bisschen an der Zeichenmethode arbeiten, besonders im Bezug auf disposen etc :)
    Ich weiß :) Wie gesagt, ich kämpfe mich grad durch den GDI Mist und hab den Hauptaugenmerk noch nicht beim Feintuning.
    "Gib einem Mann einen Fisch und du ernährst ihn für einen Tag. Lehre einen Mann zu fischen und du ernährst ihn für sein Leben."

    Wie debugge ich richtig? => Debuggen, Fehler finden und beseitigen
    Wie man VisualStudio nutzt? => VisualStudio richtig nutzen
    Ja mit deinem Projekt :)
    Ich habe ganz schnell das gewünsche Control von dir gebastelt, vielleicht hilft dir der Code weiter, kannst ihn jedoch nicht 1zu1 verwenden da er vb.net ist und nicht c#. (außer du konvertierst)

    Edit:// oookey, ich korregiere, der Fehler ist doch vorhanden.

    Mehr dazu in einem Thread von mir, aus dem der Basiscode entnommen wurde:
    Controlsammlung: SteamControls - Ein modernes DarkUI für deine Anwendung!

    Dateien
    • GlossyButton.vb

      (6,47 kB, 146 mal heruntergeladen, zuletzt: )
    • Helpers.vb

      (1,38 kB, 150 mal heruntergeladen, zuletzt: )
    Mfg: Gather
    Private Nachrichten bezüglich VB-Fragen werden Ignoriert!


    Das Problem ist das .ClipRectangle.
    Denn es wird nicht das Ganze Control neugezeichnet sondern nur die vorher unsichtbaren Teile.
    Das ClipRectangle gibt dir auch nur den Bereich zurück.

    Also das X der Location ist entsprechend höher. Also malst du dann jedesmal nur in ein kleineres Rectangle wieder alles rein.
    Mit anderen Worten, verwende ClipRectangle nur bei sehr komplexen Controls. Windows Forms clippen sowieso selbstständig Teile die du zeichnest.

    LG

    //Edit:

    Außerdem würde ich das OnPaint Event direkt überschreiben und nicht das PaintEvent abonnieren.
    LG
    Das ist meine Signatur und sie wird wunderbar sein!

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

    Gonger96 schrieb:

    Probier mal dem Control ResizeRedraw per SetStyles zu geben. >

    ​Wo genau?

    Im Konstruktor des Knopfs...
    Spoiler anzeigen

    C#-Quellcode

    1. public GlossyKnopf()
    2. {
    3. InitializeComponent();
    4. this.DoubleBuffered = true;
    5. this.FlatAppearance.BorderSize = 20;
    6. this.FlatAppearance.BorderColor = Color.Black;
    7. this.SetStyle(System.Windows.Forms.ControlStyles.ResizeRedraw, true);
    8. }


    ...sowie in der Designer.cs Datei hat das nix gebracht...
    Spoiler anzeigen

    C#-Quellcode

    1. private void InitializeComponent()
    2. {
    3. components = new System.ComponentModel.Container();
    4. this.Paint += new System.Windows.Forms.PaintEventHandler(this.Zeichnen);
    5. this.MouseDown += new System.Windows.Forms.MouseEventHandler(this.KlickAnfang);
    6. this.MouseUp += new System.Windows.Forms.MouseEventHandler(this.KlickEnde);
    7. this.MouseLeave += new System.EventHandler(this.MausRunter);
    8. this.MouseEnter += new System.EventHandler(this.MausDrauf);
    9. this.SetStyle(System.Windows.Forms.ControlStyles.ResizeRedraw, true);
    10. }


    Alternativ die Form einfach mal minimieren und dann wieder einblenden. Sieht der Button dann normal aus, malst du nicht neu bei einer Größenänderung <img src="http://www.vb-paradise.de/wcf/images/smilies/wink.png" alt=";)" />


    ​Ja, nach dem maximieren sieht der wieder hübsch aus.
    "Gib einem Mann einen Fisch und du ernährst ihn für einen Tag. Lehre einen Mann zu fischen und du ernährst ihn für sein Leben."

    Wie debugge ich richtig? => Debuggen, Fehler finden und beseitigen
    Wie man VisualStudio nutzt? => VisualStudio richtig nutzen
    @mrMo Machst Du einfach in void Zeichnen

    C#-Quellcode

    1. Rectangle rect = this.ClientRectangle;// e.ClipRectangle;

    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!
    wie Mono schon richtig erkannt hat verwendest du besser nicht Rectangle rect = e.ClipRectangle (GlossyKnopf.cs, Zeile 37) sondern :

    Rectangle rect = new Rectangle(0,0,this.Width,this.Height);

    Edit: ok RodFromGermany war schneller und seine Antwort war auch sinnvoller
    If Energy = Low Then
    Drink(aHugeCoffee)
    Else
    Drink(aHugeCoffeeToo)
    End If

    RodFromGermany schrieb:


    C#-Quellcode

    1. Rectangle rect = this.ClientRectangle;// e.ClipRectangle;


    Ok, dass ist der Grund warum ich hier nachgefragt habe.
    Läuft wie es soll, top! Der super schöne GlossyKnopf bleibt am Stück :)

    Edit:

    Mono schrieb:


    Also das X der Location ist entsprechend höher. Also malst du dann jedesmal nur in ein kleineres Rectangle wieder alles rein.
    Mit anderen Worten, verwende ClipRectangle nur bei sehr komplexen Controls. Windows Forms clippen sowieso selbstständig Teile die du zeichnest.


    Ok, super jetzt hab ich auch den Hintergrund dazu, sehr nice. Besten Dank
    "Gib einem Mann einen Fisch und du ernährst ihn für einen Tag. Lehre einen Mann zu fischen und du ernährst ihn für sein Leben."

    Wie debugge ich richtig? => Debuggen, Fehler finden und beseitigen
    Wie man VisualStudio nutzt? => VisualStudio richtig nutzen
    Hi
    noch eine kleine Randnotiz: Für Steuerelement-Inhalte greift man idR. auf die TextRenderer-Klasse zurück. Das bietet einem die Möglichkeit, das Verhalten über Flags zu definieren und zeichnet Text so, wie es auch Button, usw. haben, d.h. z.B. "&OK" wird dann zu OK. Sofern man nicht das entsprechende Flag setzt.

    Das ClipRectangle würde man tatsächlich nur verwenden, um Inhalte, die nicht gezeichnet werden müssen, von vornherein auszuschließen - was sich erst bei großen Steuerelementen lohnt oder um die Initialisierung unbenötigter Objekte zu verhindern.

    Statt den Font zu erzeugen, verwende doch den durch die Font-Eigenschaft bereitgestellten. Dispose außerdem jene Objekte, die du konstruierst, d.h. LinearGradientBrush, usw.

    Viele Grüße
    ~blaze~

    ~blaze~ schrieb:

    verwende doch den durch die Font-Eigenschaft bereitgestellten.
    Du meinst die des Buttons von dem geerbt wurde?

    TextRenderer-Klasse
    Ah ok, schau ich mir an.

    Dispose außerdem jene Objekte, die du konstruierst, d.h. LinearGradientBrush, usw.
    ist bereits nach dem Lösen meines Problems erledigt worden.
    "Gib einem Mann einen Fisch und du ernährst ihn für einen Tag. Lehre einen Mann zu fischen und du ernährst ihn für sein Leben."

    Wie debugge ich richtig? => Debuggen, Fehler finden und beseitigen
    Wie man VisualStudio nutzt? => VisualStudio richtig nutzen
    Dispose mit Using? Bei Fehlern sollte man das Objekt trotz allem freigeben, d.h. entweder Try-Finally mit Dispose oder Using, was das gleiche macht - verwende Using, sofern möglich (in deinem Fall ist es möglich).

    Control selbst stellt bereits die Font-Eigenschaft bereit, aber ja, diese.

    Viele Grüße
    ~blaze~
    @~blaze~ Wird aktuell per .Dispose() gemacht. Bin kein Freund von try/catch/finally. Die Using Geschichte nutze ich leider noch zu selten. Ist bei mir einfach noch nicht im Workflow angekommen, muss ich mir unbedingt angewöhnen...

    Stimmt, im Designer kann man ja schon Schriftarten setzen. Oh man, da hätte ich auch drauf kommen können. Jetzt hab ich mir das selbst dort rein gebaut ;)

    Btw. was passiert, wenn ich das nicht dispose? Nachdem das Event durch ist sollte doch der GC das Zeug weg rumen, oder nicht?
    "Gib einem Mann einen Fisch und du ernährst ihn für einen Tag. Lehre einen Mann zu fischen und du ernährst ihn für sein Leben."

    Wie debugge ich richtig? => Debuggen, Fehler finden und beseitigen
    Wie man VisualStudio nutzt? => VisualStudio richtig nutzen
    Using empfehle ich in jedem Fall, in dem etwas innerhalb einer Methode freigegeben werden sollte.
    Try-Finally ist ein Werkzeug, das in einigen Fällen nützlich ist. Übrigens nicht zu verwechseln mit Try-Catch-Finally, das zusätzlich noch manche Fehler abfängt. Try-Finally führt den Code aus und, unabhängig von dessen "Erfolg", anschließend den Finally-Block, der z.B. dazu genutzt werden kann, Daten per Dispose freizugeben.
    Entgegen der Fehlinformation (meine Meinung), die sich im Forum breitgemacht hat, auf Try-Catch zu verzichten, bin ich der Meinung, dass es durchaus wichtig ist, es zu verwenden und insbesondere zu lernen, wie man es richtig einsetzt.

    Der GC räumt nur das weg, was er wichtig findet. Wenn kleinere Datenmengen bestehenbleiben, macht ihm das herzlich wenig aus, nur muss er dafür erkennen, wie groß die Datenmengen sind. Der GC kennt aber idR. nur die Größe der Daten, die er selbst angelegt hat und wenn er große Objekte als klein einschätzt, weil unverwalteter Speicher belegt wurde - was er ja nicht erkannt hat - bleiben diese vorhanden.
    Außerdem ist laut Konventionen jedes IDisposable implementierende Objekt zu einem sinnvollen Zeitpunkt freizugeben.

    Viele Grüße
    ~blaze~
    Try/Finally kannte ich bis dato ohne den Catch nicht. Gut zu wissen. Wo es geht, versuche ich auf Try-Catch zu verzichten, wenn es nicht anders geht, geht es halt auch nicht anders. Jedoch halte ich von dem allseits beliebten inflationellen Einsatz nichts. Aber dieses Thema wurde an einigen Stellen bereits behandelt, lassen wir das gut sein :)
    "Gib einem Mann einen Fisch und du ernährst ihn für einen Tag. Lehre einen Mann zu fischen und du ernährst ihn für sein Leben."

    Wie debugge ich richtig? => Debuggen, Fehler finden und beseitigen
    Wie man VisualStudio nutzt? => VisualStudio richtig nutzen

    ~blaze~ schrieb:

    Für Steuerelement-Inhalte greift man idR. auf die TextRenderer-Klasse zurück. Das bietet einem die Möglichkeit, das Verhalten über Flags zu definieren und zeichnet Text so, wie es auch Button, usw. haben, d.h. z.B. "&OK" wird dann zu OK. Sofern man nicht das entsprechende Flag setzt.


    Ok, ist drin. Nur, was muss ich tun, dass der Button ausgelöst wird sobald ich z.B. alt + b drücke? Wird wohl an den ​TextFormatFlags hängen, oder?

    C#-Quellcode

    1. TextRenderer.DrawText(e.Graphics, _Text, Font, rect, ForeColor);

    Im Designer wird mit z.B. das "b" unterstrichen, zur Laufzeit passiert dann nix mehr.
    "Gib einem Mann einen Fisch und du ernährst ihn für einen Tag. Lehre einen Mann zu fischen und du ernährst ihn für sein Leben."

    Wie debugge ich richtig? => Debuggen, Fehler finden und beseitigen
    Wie man VisualStudio nutzt? => VisualStudio richtig nutzen
    @RodFromGermany

    mrMo schrieb:

    (...) was muss ich tun, dass der Button ausgelöst wird sobald ich z.B. alt + b drücke?


    Welchen TextFormatFlag muss ich setzen? Dieses mal werde ich aus der MSDN nicht schlau.
    "Gib einem Mann einen Fisch und du ernährst ihn für einen Tag. Lehre einen Mann zu fischen und du ernährst ihn für sein Leben."

    Wie debugge ich richtig? => Debuggen, Fehler finden und beseitigen
    Wie man VisualStudio nutzt? => VisualStudio richtig nutzen