Wiedergeben eines Windows Forms-Steuerelements

Unter Rendering wird der Prozess der Erstellung einer visuellen Darstellung auf dem Bildschirm eines Benutzers verstanden. Windows Forms verwendet GDI (die neue Windows-Grafikbibliothek) für das Rendering. Die verwalteten Klassen, die den Zugriff auf GDI ermöglichen, befinden sich im System.Drawing-Namespace und seinen untergeordneten Namespaces.

Beim Rendern von Steuerelementen sind die folgenden Elemente beteiligt:

  • Die von der Basisklasse System.Windows.Forms.Control bereitgestellte Funktion zum Zeichnen

  • Die essentiellen Elemente der GDI-Grafikbibliothek

  • Die Geometrie der Zeichnungsregion

  • Die Prozedur für das Freigeben von Grafikressourcen

Vom Steuerelement bereitgestellte Funktionen zum Zeichnen

Die Basisklasse Control stellt Funktionen zum Zeichnen über das dazugehörige Paint-Ereignis bereit. Ein Steuerelement löst das Paint-Ereignis aus, sobald die dazugehörige Anzeige aktualisiert werden muss. Weitere Informationen zu Ereignissen in .NET Framework finden Sie unter Behandeln und Auslösen von Ereignissen.

Die Ereignisdatenklasse für das Paint-Ereignis, PaintEventArgs, enthält die Daten, die für das Zeichnen eines Steuerelements erforderlich sind – ein Handle für ein Grafikobjekt und ein Rechteckobjekt, das die Region darstellt, in der gezeichnet wird. Diese Objekte sind im folgenden Codefragment fett gedruckt.

Public Class PaintEventArgs  
   Inherits EventArgs  
   Implements IDisposable  
  
   Public ReadOnly Property ClipRectangle() As System.Drawing.Rectangle  
      ...  
   End Property  
  
   Public ReadOnly Property Graphics() As System.Drawing.Graphics  
      ...  
   End Property  
   ' Other properties and methods.  
   ...  
End Class  
public class PaintEventArgs : EventArgs, IDisposable {  
public System.Drawing.Rectangle ClipRectangle {get;}  
public System.Drawing.Graphics Graphics {get;}  
// Other properties and methods.  
...  
}  

Graphics ist eine verwaltete Klasse, die die Funktion zum Zeichnen kapselt. Weitere Informationen finden Sie in der GDI-Diskussion später in diesem Thema. Bei ClipRectangle handelt es sich um eine Instanz der Rectangle-Struktur. Hierdurch wird der verfügbare Bereich definiert, in dem ein Steuerelement zeichnen kann. Der Entwickler eines Steuerelements kann ClipRectangle mit der ClipRectangle-Eigenschaft eines Steuerelements berechnen. Weitere Informationen finde Sie in der Geometriediskussion später in diesem Thema.

Ein Steuerelement muss Renderinglogik bereitstellen, indem die OnPaint-Methode überschrieben wird, die es von Control erbt. OnPaint erhält über die übergebenen Eigenschaften Graphics und ClipRectangle der Instanz PaintEventArgs Zugriff auf ein Grafikobjekt und ein Rechteck, in dem gezeichnet werden kann.

Protected Overridable Sub OnPaint(pe As PaintEventArgs)  
protected virtual void OnPaint(PaintEventArgs pe);  

Die OnPaint-Methode der Control-Basisklasse implementiert keine Zeichnungsfunktionen, ruft jedoch die Ereignisdelegaten auf, die für das Paint-Ereignis registriert sind. Wenn Sie OnPaint überschreiben, sollten Sie in der Regel die OnPaint-Methode der Basisklasse aufrufen, damit registrierte Delegaten das Paint-Ereignis empfangen. Steuerelemente, deren gesamte Oberfläche gezeichnet wird, sollten nicht die OnPaint-Methode der Basisklasse aufrufen, da dies zu Flimmern führen kann. Ein Beispiel für das Außerkraftsetzen des OnPaint-Ereignisses finden Sie in Vorgehensweise: Erstellen eines Windows Forms-Steuerelements, das den Fortschritt anzeigt.

Hinweis

Rufen Sie OnPaint nicht direkt in Ihrem Steuerelement auf. Rufen Sie stattdessen die Invalidate-Methode (von Control geerbt) oder eine andere Methode auf, die Invalidate aufruft. Die Invalidate-Methode wiederum ruft OnPaint auf. Die Invalidate-Methode wird überladen und ein Steuerelement zeichnet je nach für Invalidatee bereitgestellten Argumenten entweder Teile oder den gesamten Bildschirmbereich neu.

Die Control-Basisklasse definiert eine weitere Methode, die hilfreich für das Zeichnen ist: die OnPaintBackground-Methode.

Protected Overridable Sub OnPaintBackground(pevent As PaintEventArgs)  
protected virtual void OnPaintBackground(PaintEventArgs pevent);  

OnPaintBackground zeichnet den Hintergrund (und so auch die Form) des Fensters. Dies erfolgt sehr schnell. OnPaint hingegen zeichnet die Details und ist deshalb möglicherweise langsamer, da einzelne Zeichnungsanforderungen in ein Paint-Ereignis kombiniert werden, das alle Bereiche abdeckt, die neu gezeichnet werden müssen. Sie sollten OnPaintBackground aufrufen, wenn Sie beispielsweise ein Hintergrund mit Farbverlauf für Ihr Steuerelement zeichnen möchten.

Während OnPaintBackground eine ereignisähnliche Bezeichnung aufweist und dasselbe Argument wie die OnPaint-Methode annimmt, handelt es sich bei OnPaintBackground um keine tatsächliche Ereignismethode. Es gibt kein PaintBackground-Ereignis, und OnPaintBackground ruft keine Ereignisdelegaten auf. Wenn die OnPaintBackground-Methode überschrieben wird, ist keine abgeleitete Klasse erforderlich, um die OnPaintBackground-Methode der dazugehörigen Basisklasse aufzurufen.

GDI+-Grundlagen

Die Graphics-Klasse bietet Methoden für das Zeichnen verschiedener Formen wie Kreise, Dreiecke, Bögen und Ellipsen sowie Methoden für das Anzeigen von Text. Der System.Drawing-Namespace und seine untergeordneten Namespaces enthalten Klassen, die grafische Elemente wie Formen (z. B. Kreise, Rechtecke und Bögen), Farben, Schriftarten und Pinsel kapseln. Weitere Informationen über GDI finden Sie unter Verwenden von verwalteten Grafikklassen. Die Grundlagen von GDI werden auch im Artikel Vorgehensweise: Erstellen eines Windows Forms-Steuerelements, das den Fortschritt anzeigt beschrieben.

Geometrie der Zeichnungsregion

Die ClientRectangle-Eigenschaft eines Steuerelements gibt die rechteckige Region an, die für das Steuerelement auf der Anzeige für den Benutzer verfügbar ist. Die ClipRectangle-Eigenschaft von PaintEventArgs gibt dahingegen den Bereich an, der tatsächlich gezeichnet wird. (Denken Sie daran, dass das Zeichnen in der Paint-Ereignismethode erfolgt, die eine PaintEventArgs-Instanz als Argument übernimmt). Ein Steuerelement muss möglicherweise nur einen Teil des verfügbaren Bereichs zeichnen, z. B. wenn ein kleiner Bereich der Anzeige des Steuerelements geändert wird. In solchen Situationen muss der Entwickler eines Steuerelements das tatsächliche Rechteck berechnen, in dem gezeichnet werden soll, und dieses an Invalidate übergeben. Die überladenen Versionen von Invalidate, die Rectangle oder Region als Argument akzeptieren, verwenden dieses Argument, um die ClipRectangle-Eigenschaft von PaintEventArgs zu generieren.

Das folgende Codefragment zeigt, wie das benutzerdefinierte FlashTrackBar-Steuerelement den rechteckigen Bereich berechnet, in den gezeichnet werden soll. Die Variable client kennzeichnet die ClipRectangle-Eigenschaft. Ein vollständiges Beispiel finden Sie unter Vorgehensweise: Erstellen eines Windows Forms-Steuerelements, das den Fortschritt anzeigt.

Rectangle invalid = new Rectangle(
    client.X + min,
    client.Y,
    max - min,
    client.Height);

Invalidate(invalid);
Dim invalid As Rectangle = New Rectangle( _
    client.X + lmin, _
    client.Y, _
    lmax - lmin, _
    client.Height)

Invalidate(invalid)

Freigeben von Grafikressourcen

Grafikobjekte sind teuer, da sie Systemressourcen verwenden. Zu solchen Objekten gehören Instanzen der System.Drawing.Graphics-Klasse und Instanzen von System.Drawing.Brush, System.Drawing.Pen sowie weitere Grafikklassen. Es ist wichtig, dass Sie nur eine Grafikressource zeichnen, wenn Sie eine benötigen, und dass Sie sie freigeben, sobald Sie die Verwendung beendet haben. Wenn Sie einen Typen erstellen, der die IDisposable-Schnittstelle implementiert, rufen Sie die dazugehörige Dispose-Methode auf, wenn Sie mit ihm fertig sind, um Ressourcen freizugeben.

Das folgende Codefragment zeigt, wie das benutzerdefinierte FlashTrackBar-Steuerelement eine Brush-Ressource erstellt und freigibt. Den vollständigen Quellencode finden Sie unter Vorgehensweise: Erstellen eines Windows Forms-Steuerelements, das den Fortschritt anzeigt.

private Brush baseBackground = null;
Private baseBackground As Brush
base.OnPaint(e);
if (baseBackground == null) {
    if (showGradient) {
        baseBackground = new LinearGradientBrush(new Point(0, 0),
                                                 new Point(ClientSize.Width, 0),
                                                 StartColor,
                                                 EndColor);
    }
    else if (BackgroundImage != null) {
        baseBackground = new TextureBrush(BackgroundImage);
    }
    else {
        baseBackground = new SolidBrush(BackColor);
    }
}
MyBase.OnPaint(e)

If (baseBackground Is Nothing) Then

    If (myShowGradient) Then
        baseBackground = New LinearGradientBrush(New Point(0, 0), _
                                                 New Point(ClientSize.Width, 0), _
                                                 StartColor, _
                                                 EndColor)
    ElseIf (BackgroundImage IsNot Nothing) Then
        baseBackground = New TextureBrush(BackgroundImage)
    Else
        baseBackground = New SolidBrush(BackColor)
    End If

End If
protected override void OnResize(EventArgs e) {
    base.OnResize(e);
    if (baseBackground != null) {
        baseBackground.Dispose();
        baseBackground = null;
    }
}
Protected Overrides Sub OnResize(ByVal e As EventArgs)
    MyBase.OnResize(e)
    If (baseBackground IsNot Nothing) Then
        baseBackground.Dispose()
        baseBackground = Nothing
    End If
End Sub

Weitere Informationen