Rendering di un controllo Windows Form

Il rendering rappresenta il processo di creazione di una rappresentazione visiva sullo schermo dell'utente. In Windows Form per il rendering viene utilizzato GDI, la nuova libreria grafica di Windows. Le classi gestite che forniscono l'accesso a GDI si trovano nello spazio dei nomi System.Drawing e nei relativi sottospazi dei nomi.

Di seguito sono indicati gli elementi interessati dal rendering dei controlli.

  • Le funzionalità di disegno fornite dalla classe base System.Windows.Forms.Control.

  • Gli elementi essenziali della libreria grafica GDI.

  • La geometria della regione di disegno.

  • La procedura per la liberazione di risorse grafiche.

Funzionalità di disegno fornite da Control

La classe base Control fornisce funzionalità di disegno tramite l'evento Paint. L'evento Paint viene generato dal controllo ogni volta che è necessario aggiornare la relativa visualizzazione. Per ulteriori informazioni sugli eventi di .NET Framework, vedere Gestione e generazione di eventi.

La classe dei dati evento per Paint, PaintEventArgs, contiene i dati necessari per il disegno di un controllo, un handle a un oggetto grafico e un oggetto rettangolo che rappresenta la regione in cui eseguire il disegno. Questi oggetti sono indicati in grassetto nel codice riportato di seguito.

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 è una classe gestita che incapsula funzionalità di disegno, come descritto a proposito di GDI più avanti in questo argomento. La proprietà ClipRectangle è un'istanza della struttura Rectangle e definisce l'area disponibile per il disegno di un controllo. Gli sviluppatori possono calcolare ClipRectangle attraverso la proprietà ClipRectangle di un controllo, come descritto a proposito della geometria più avanti in questo argomento.

È necessario che un controllo fornisca logica di rendering mediante l'override del metodo OnPaint ereditato dalla classe Control. Il metodo OnPaint accede all'oggetto grafico e al rettangolo in cui effettuare il disegno tramite le proprietà Graphics e ClipRectangle dell'istanza PaintEventArgs a esso passata.

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

Il metodo OnPaint della classe base Control non implementa alcuna funzionalità di disegno ma richiama unicamente i delegati registrati con l'evento Paint. Quando si sottopone a override OnPaint, è in genere opportuno richiamare il metodo OnPaint della classe base in modo che i delegati registrati ricevano l'evento Paint. Per i controlli di cui viene disegnata l'intera superficie è tuttavia opportuno non richiamare il metodo OnPaint della classe base, in quanto si verificherebbe uno sfarfallio. Per un esempio dell'override dell'evento OnPaint, vedere Procedura: creare un controllo di Windows Form che visualizzi lo stato di avanzamento.

Nota

Non richiamare il metodo OnPaint direttamente dal controllo. Richiamare invece il metodo Invalidate, ereditato dalla classe Control, o un altro metodo che richiami Invalidate. Il metodo Invalidate richiamerà a sua volta OnPaint. Il metodo Invalidate e è sottoposto a overload e, a seconda degli argomenti a esso forniti, l'area visualizzata di un controllo viene ridisegnata interamente o in parte.

La classe base Control definisce un altro metodo utile per il disegno: il metodo OnPaintBackground.

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

Il metodo OnPaintBackground disegna lo sfondo, e di conseguenza la forma, della finestra ed è sempre veloce, mentre il metodo OnPaint disegna i dettagli e può risultare più lento, in quanto tutte le singole richieste di disegno vengono combinate in un unico evento Paint che copre tutte le aree da ridisegnare. È possibile richiamare il metodo OnPaintBackground se, ad esempio, si desidera applicare al controllo uno sfondo di colore sfumato.

Mentre il metodo OnPaintBackground presenta una nomenclatura analoga a quella degli eventi e accetta lo stesso argomento del metodo OnPaint, OnPaintBackground non è un vero metodo di evento. Non esiste infatti alcun evento PaintBackground e il metodo OnPaintBackground non richiama delegati di evento. Quando si sottopone a override il metodo OnPaintBackground, non è necessaria alcuna classe derivata per richiamare il metodo OnPaintBackground della relativa classe base.

Nozioni fondamentali su GDI+

La classe Graphics fornisce metodi per il disegno di varie forme, quali cerchi, triangoli, archi ed ellissi, nonché metodi per la visualizzazione di testo. Lo spazio dei nomi System.Drawing e i relativi sottospazi dei nomi contengono classi che incapsulano elementi grafici quali forme (cerchi, rettangoli, archi e altre), colori, tipi di carattere, pennelli e così via. Per ulteriori informazioni su GDI, vedere Utilizzo di classi grafiche gestite. Per informazioni di base su GDI, vedere inoltre Procedura: creare un controllo di Windows Form che visualizzi lo stato di avanzamento.

Geometria della regione di disegno

La proprietà ClientRectangle di un controllo specifica la regione rettangolare a disposizione del controllo sullo schermo dell'utente, mentre la proprietà ClipRectangle della classe PaintEventArgs specifica l'area effettivamente disegnata. Tenere presente che il disegno viene eseguito nel metodo per l'evento Paint cui viene fornito come argomento un'istanza di PaintEventArgs. Può essere necessario ridisegnare solo una parte dell'area disponibile a un controllo, ad esempio nel caso in cui venga modificata solo una piccola sezione del controllo visualizzato. In queste situazioni lo sviluppatore del controllo deve calcolare l'effettivo rettangolo in cui è necessario disegnare e passarlo a Invalidate. Le versioni sottoposte a overload del metodo Invalidate cui viene fornito come argomento Rectangle o Region utilizzano l'argomento per generare la proprietà ClipRectangle della classe PaintEventArgs.

Nel codice seguente viene illustrato come calcolare nel controllo personalizzato FlashTrackBar l'area rettangolare in cui si esegue il disegno. La variabile client denota la proprietà ClipRectangle. Per un esempio completo, vedere Procedura: creare un controllo di Windows Form che visualizzi lo stato di avanzamento.

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

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

Invalidate(invalid);

Liberazione di risorse grafiche

Gli oggetti grafici utilizzano molte risorse di sistema. Questi oggetti includono istanze della classe System.Drawing.Graphics, nonché istanze delle classi System.Drawing.Brush, System.Drawing.Pen e altre classi di grafica. È importante creare una risorsa grafica solo quando è effettivamente necessaria e rilasciarla appena si termina di utilizzarla. Se si crea un tipo che implementa l'interfaccia IDisposable, chiamare il relativo metodo Dispose quando si cessa di utilizzarla, in modo da liberare risorse.

Nel codice seguente viene illustrato come viene creata e rilasciata una risorsa Brush dal controllo personalizzato FlashTrackBar. Per il codice sorgente completo, vedere Procedura: creare un controllo di Windows Form che visualizzi lo stato di avanzamento.

Private baseBackground As Brush
private Brush baseBackground = null;
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 Not (BackgroundImage Is Nothing) Then
        baseBackground = New TextureBrush(BackgroundImage)
    Else
        baseBackground = New SolidBrush(BackColor)
    End If

End If
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);
    }
}
Protected Overrides Sub OnResize(ByVal e As EventArgs)
    MyBase.OnResize(e)
    If Not (baseBackground Is Nothing) Then
        baseBackground.Dispose()
        baseBackground = Nothing
    End If
End Sub
protected override void OnResize(EventArgs e) {
    base.OnResize(e);
    if (baseBackground != null) {
        baseBackground.Dispose();
        baseBackground = null;
    }
}

Vedere anche

Attività

Procedura: creare un controllo di Windows Form che visualizzi lo stato di avanzamento