OnMouseMove in UserControl- this.Invalidate() leistungshungrig?



  • Th69 schrieb:

    Und alle "managed resources", wie z.B. Pen, Brush, etc. solltest du NICHT im Paint-Ereignis erzeugen ,
    sondern besser in einer Membervariablen ablegen (d.h. im Konstruktor erzeugen und dann im Paint nur noch benutzen).
    Ansonsten nimmst du Windows u.U. viele "Handles" weg und der GC hat dann die Arbeit...

    Danke werde ich machen! Habe diese, wie ja bereits geschrieben, immer wieder im OnPaint oder dort wo ich diese gebraucht habe erzeugt. Hier zwei Methode die im OnPaint aufgerufen werden, noch ungeändert(so wie ich sie bisher implementiert hatte):

    private void DrawSelectionWindow(Graphics g)
    {
        Pen myPen = new Pen(System.Drawing.SystemBrushes.WindowFrame);
        myPen.DashStyle = System.Drawing.Drawing2D.DashStyle.Dot;
    
        Rectangle rec = new Rectangle(Math.Min(pLeftMousedown.X,p2_SelectionWindow.X),Math.Min(pLeftMousedown.Y,p2_SelectionWindow.Y),Math.Abs(p2_SelectionWindow.X-pLeftMousedown.X),Math.Abs(p2_SelectionWindow.Y-pLeftMousedown.Y));
        g.DrawRectangle(myPen, rec);            
    }
    

    eine andere Methode:

    public void Draw(Graphics g)
    {
       foreach (Connection c in this.ConnectionList)
       {
         Pen linePen = new Pen(System.Drawing.Color.Black, (float)(2));
         g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
         g.DrawLine(linePen, c.D1.GetConnectionPoint(c), c.D2.GetConnectionPoint(c));
       }
    
       foreach (Device d in this)
       {
         if (!d.Selected)
         {   //marked devices
             d.SetResolution(g.DpiX, g.DpiY);
             g.DrawImageUnscaled(d.Symbol, d.Position);
         }
       }
       foreach (Device d in this)
       {
         if (d.Selected)
         {   //marked devices
             Pen myPen = new Pen(System.Drawing.SystemBrushes.ControlDarkDark);
             d.SetResolution(g.DpiX, g.DpiY);
             g.DrawImageUnscaled(d.Symbol, d.Position);
             g.DrawRectangle(myPen, d.PositionX, d.PositionY, d.Symbol.Width, d.Symbol.Height);
         }
       }
    }
    

    Th69 schrieb:

    Gleiches gilt natürlich auch für andere sich wiederholende Aufrufe, z.B. Timern etc.

    Gehören da auch Rectangles dazu?? denn die müssen ja immer wieder erzeugt werden da sie immer unterschiedlich sind. Benutze Rectangles für ein SelectionWindow und dieses ist ja von der Mausposition abhängig und daher muss ja dauernd ein neues erstellt werden.

    Th69 schrieb:

    (und wieder disposen - ich hoffe mal wenigstens, du benutzt bisher "using ...")

    Hmm, verstehe ich nicht ganz was du meinst? Warum soll ich mitten im Programmcode using verwenden. Könntest du mir das erklären.

    Das mit Dispose wurde hier: Dispose in C# besprochen. Daher werde ich in Zukunft das Dispose für die Pens und die Brushes auch ausführen wenn die Methode von den Objekten angeboten wird.

    Lg und besten Dank für eure Hilfe!



  • Du solltest using verwenden, weil es dafür sorgt dass deine Objekte disposed werden auch wenn eine Exception fliegt, so z.B:

    using(Pen pen = new ...)
    {
      // Zeichnen
    }
    

    Das ganze wird dann umgesetzt was in etwa so aussieht:

    Pen pen = new ...
    try
    {
      // Zeichnen
    }
    finally
    {
      pen.Dispose();
    }
    

    Wenn jetzt beim Zeichnen irgendetwas schief geht wird der Pen trotzdem freigeben und using sieht einfach besser aus als diese hässlichen try-finally -Blöcke. 🤡

    Der Pen ist hier jetzt nur ein Beispiel. Für einen Pen wäre es sinnvoller ihn einmal zu erstellen und in einer Membervariable der Klasse zwischen zu speichern und beim nächsten Zeichnen wiederzuverwenden.



  • THE_ONE schrieb:

    Pen linePen = new Pen(System.Drawing.Color.Black, (float)(2));
    

    Sicher nur ein Detail und in dem Zusammenhang belanglos für die Performance, aber...

    Anstatt

    (float)(2)
    

    wäre es besser einfach

    2.0f
    

    zu schreiben. Im ersten Fall erzeugst Du erst ein termporäres Objekt vom Type int und wandelst das dann in einen float um was keine triviale Konvertierung ist. Im zweiten Fall erzeugst Du einfach einen float...

    also:

    Pen linePen = new Pen(System.Drawing.Color.Black, 2.0f);
    


  • zuersteinmal zu 'loks' Aussage:
    das ist völlig egal, ob 2.0f oder (float)2, da das der Compiler automatisch umwandelt... (du kannst auch (float)(1+1) schreiben)

    und nun wieder zu 'THE ONE':
    hatte ich also richtig geraten, daß du die 'using'-Anweisung noch nicht kanntest (aber 'O.o' hat sie ja schon richtig beschrieben).

    Edit: nicht daß du es jetzt falsch verstehst, generell sollte man bei temporären Klassen, welche dispose() implementieren, 'using(...)' benutzen (besser als dispose() von Hand aufzurufen).
    Für deine konstanten Pens, Brushes etc. sollten diese jedoch trotzdem als Membervariablen angelegt werden!!!

    Und ein Rectangle ist nur eine einfache Struktur, d.h. dort ist die Performance relativ unwichtig (wobei es schon sinnvoll ist, auch diese nur einmalig zu erstellen, sofern sie konstant ist - in deinem Beispiel ist das Rectangle aber abhängig von der Mausposition, daher mußt du es ja immer wier neu berechnen).

    Außerdem kommt mir noch "foreach(Device d in this)" komisch vor, d.h. hast du keine Trennung zwischen Logik und GUI???



  • Das mit using glaube ich jetzt vestanden zu haben:
    http://msdn.microsoft.com/de-de/library/yh598w02(VS.80).aspx
    http://blog.schelian.de/2008/01/10/usingKurzErlaumlutertC.aspx

    Th69 schrieb:

    Außerdem kommt mir noch "foreach(Device d in this)" komisch vor, d.h. hast du keine Trennung zwischen Logik und GUI???

    Eigentlich versuche ich schon auf sowas zu achten, weil ich mal vom 3Tier Modell gehört habe! Nur irgendwo muss es doch die Schnittstelle zwischen GUI und Logik geben. (die erste Methode von vorher geört zur GUI die zweite gehört eigentlich zu einer anderen Klasse mit Logik). Wie man das am besten handled würde ich eh gerne mal wissen.
    Außerdem hängt die GUI in meinem Fall sehr eng mit der Logik zusammen. Bisher besteht mein Programm eigentlich nur aus GUI und GUILogik. Die eigentliche Logik kommt erst später hinzu und ist DANN eigentlich ziemlich easy zu implementieren. Da alles was auf dem Bildschirm ist auch mit meiner Logik zu tun hat. Ändert sich was auf dem Schirm, ändert sich auch was in der Logik.

    Trotz all meines Geschwafels muss ich aber zugeben das ich wenig Programmiererfahrung habe und dass mir eigentlich noch nie jemand gesagt hat wie ich etwas besser machen könnte. Mein bisheriges Wissen habe ich alles aus Büchern.

    Daher finde ich dieses Forum auch so hilfreich. Da wird man auf Dinge aufmerksam gemacht auf die man sonst gar nicht kommen würde. Bis vor kurzem kannte ich weder Dispose, Using, und noch so einiges mehr.
    Daher Danke an alle die in diesem Forum so aktiv mitarbeiten und uns NOOBS helfen besseren Code zu schreiben.



  • Th69 schrieb:

    das ist völlig egal, ob 2.0f oder (float)2, da das der Compiler automatisch umwandelt... (du kannst auch (float)(1+1) schreiben)

    Dir ist schon bewusst das int nach float kein simpler cast ist sondern eine aufwendige Umrechnung?

    btw, wenn man explizit den cast angeben muß dann ist das per Definition keine automatische Umwandlung.



  • O.o schrieb:

    Für einen Pen wäre es sinnvoller ihn einmal zu erstellen und in einer Membervariable der Klasse zwischen zu speichern und beim nächsten Zeichnen wiederzuverwenden.

    Wo führe ich dann das Dispose aus?? Muss ich da einen eigenen Destruktor schreiben wo ich dann die Membervariable dispose?



  • Es gibt dafür das sogenannte Dispose-Pattern, dass auch von Microsoft vorgeschlagen wird. Ob das jetzt die beste Lösung für das Problem ist, darüber lässt sich streiten, aber es ist auf jeden Fall ein guter Anfang:

    class MyClass : IDisposable
    {
      private bool _disposed = false;
    
      ~MyClass()
      {
        Dispose(false);
      }
    
      public void Dispose()
      {
        Dispose(true);
        GC.SuppressFinalize(this);
      }
    
      private void Dispose(bool disposing)
      {
        if(!_disposed)
        {
          if(disposing)
          {
            // Managed Resourcen werden hier freigegeben Pens z.B.
          }
    
          // Unmanaged Resourcen werden hier freigegeben IntPtr z.B.
          _disposed = true;
        }
      }
    }
    


  • Hallo loks,

    dann schau dir einfach mal den IL-Code aller drei Versionen an...



  • loks schrieb:

    Th69 schrieb:

    das ist völlig egal, ob 2.0f oder (float)2, da das der Compiler automatisch umwandelt... (du kannst auch (float)(1+1) schreiben)

    Dir ist schon bewusst das int nach float kein simpler cast ist sondern eine aufwendige Umrechnung?

    btw, wenn man explizit den cast angeben muß dann ist das per Definition keine automatische Umwandlung.

    Du hast schon Recht, wenn du sagst, dass die Konvertierung nicht trivial ist. Allerdings ist der C#-Compiler inzwischen schlau genug und optimiert solche Schnitzer des Programmierers einfach weg.


Anmelden zum Antworten