Ein Weg Performance-Probleme bei Zeichenoperationen zu umgehen ist Double Buffering. Hierbei wird die jeweilige Zeichenoperation nicht direkt auf dem Bildschirm ausgeführt, sondern zunächst im Speicher. Dieser Speicher wird daraufhin extrem schnell auf in den Bildschirmspeicher kopiert. Dies beschleunigt zwar nicht wirklich die Performance, vermeidet jedoch den altbekannten Flackereffekt.
Wollte man diese Technik unter .NET 1.x nutzen, so konnte man einen in der
Control-Klasse eingebauten Mechanismus nutzen. Control bietet nämlich über die
SetStyle()-Methode die Möglichkeit Double Buffering zu aktivieren. Das folgende Beispiel demonstriert dies:
this.SetStyle(
ControlStyles.DoubleBuffer |
ControlStyles.AllPaintingInWmPaint |
ControlStyles.UserPaint |
ControlStyles.ResizeRedraw,
true);
Bei der Verwendung von Double Buffering sollten auch stets die Flags
AllPaintingInWmPaint und
UserPaint gesetzt werden. Sie weisen das Window an, keine
WM_ERASEBKGND-Meldungen zu verarbeiten (Hintergrund löschen) und signalisiert, dass sich die Anwendung um das komplette Zeichnen der Oberfläche kümmert. Ist das Fenster oder Control resizable, so kann optional über
ResizeRedraw ein automatisches Neuzeichnen bei Größenänderungen veranlasst werden.
In .NET 2.0 bietet die
ControlStyles-Enumeration nun zusätzlich den Wert
OptimizedDoubleBuffer. Dieser dient als Ersatz für
DoubleBuffer und bietet in erster Linie eine bessere Performance. Die obere Anweisung könnte in .NET 2.0 somit wie folgt umgeschrieben werden.
this.SetStyle(
ControlStyles.OptimizedDoubleBuffer |
ControlStyles.AllPaintingInWmPaint |
ControlStyles.UserPaint |
ControlStyles.ResizeRedraw,
true);
Viele der im .NET-Framework 2.0 enthaltenen Komponenten nutzen übrigens diese Methode bereits standardmässig.
Double Buffering steuern
Darüber hinaus besteht nun auch die Möglichkeit dediziert in den Double Buffering-Prozess einzugreifen. Hierzu bietet der
System.Drawing-Namespace die neuen Klassen
BufferedGraphics,
BufferedGraphicsContext und
BufferedGraphicsManager. Das folgende Beispiel demonstriert die Verwendung:
// Grafikpuffer anlegen
Graphics gr = this.CreateGraphics();
BufferedGraphicsContext bufferedContext =
BufferedGraphicsManager.Current;
bufferedContext.MaximumBuffer = this.Size;
BufferedGraphics bufferedGraphics =
bufferedContext.Allocate(gr, this.ClientRectangle);
// Auf gepufferter Grafik zeichnen
this.DrawSomething(bufferedGraphics.Graphics);
// Grafik auf den Bildschirm rendern
bufferedGraphics.Render();
Hier wird zunächst ein
Graphics-Objekt erzeugt, auf dem später gezeichnet werden soll. Daraufhin wird über
BufferedGraphicsManager.Current ein
BufferedGraphicsContext-Objekt ermittelt und dessen Puffer mit der Größe des Fensters initialisiert. Das Erzeugen des Speicherabbildes erfolgt die
Allocate()-Methode, der das
Graphics-Objekt, sowie eine
Rectangle-Struktur mit Größe und Position des Zeichenbereichs übergeben wird.
Allocate() gibt daraufhin ein
BufferedGraphics-Objekt zurück, auf dem in der Folge gezeichnet werden kann. Es bietet alle Methoden und Eigenschaften wie
Graphics, führt die Zeichenoperationen jedoch im Speicher und nicht auf dem Bildschirm aus. Für die Übertragung des Speicherabbildes auf den Bildschirm sorgt schließlich die
Render()-Methode.
Das manuelle Double Buffering lohnt sich jedoch nur für extrem aufwendige Zeichenoperationen, bei denen eine sehr feine Steuerung der Bildschirmübertragung erforderlich ist. In der Regel reicht schon der oben gezeigte
SetStyle()-Aufruf, um die Performance enorm zu verbessern.