Representar un control de formularios Windows Forms

Por representación se entiende el proceso de creación de una presentación visual en la pantalla de un usuario. Windows Forms usa GDI (la nueva biblioteca gráfica de Windows) para realizar representaciones. Las clases administradas que proporcionan acceso a GDI están en el espacio de nombres System.Drawing y sus subespacios.

Los elementos siguientes están implicados en la representación de controles:

  • La funcionalidad de dibujo que proporciona el valor System.Windows.Forms.Control de la clase base.

  • Los elementos esenciales de la biblioteca de gráficos GDI.

  • La geometría de la región de dibujo.

  • El procedimiento para liberar recursos de gráficos.

Funcionalidad de dibujo proporcionada por el control

El Control de la clase base proporciona la funcionalidad de dibujo a través de su evento Paint. Un control genera el evento Paint cada vez que necesita actualizar la presentación. Para obtener más información sobre los eventos en .NET Framework, vea Control y generación de eventos.

La clase de datos del evento Paint (PaintEventArgs) contiene los datos necesarios para dibujar un control: un manipulador de un objeto gráfico y un objeto de rectángulo que representa la región en la que se va a dibujar. Estos objetos se muestran en negrita en el siguiente fragmento de código.

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 es una clase administrada que encapsula la funcionalidad de dibujo, tal y como se describe en el análisis de GDI más adelante en este tema. ClipRectangle es una instancia de la estructura de Rectangle y define el área disponible en la que puede dibujarse un control. Un desarrollador de controles puede calcular el valor de ClipRectangle mediante la propiedad ClipRectangle de un control, tal y como se describe en el análisis de geometría más adelante en este tema.

Un control debe proporcionar la lógica de representación reemplazando el método OnPaint que hereda de Control. OnPaint obtiene acceso a un objeto gráfico y a un rectángulo en el que dibujar mediante las propiedades Graphics y ClipRectangle de la instancia de PaintEventArgs que se le ha pasado.

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

El método OnPaint de la clase base Control no implementa ninguna funcionalidad de dibujo, sino que simplemente invoca los delegados de evento que están registrados en el evento Paint. Cuando reemplace OnPaint, asegúrese de llamar al método OnPaint de la clase base de modo que los delegados registrados reciban el evento Paint. Sin embargo, los controles que representan toda la superficie no deben invocar al objeto OnPaint de la clase base, ya que este introduce parpadeo. Para obtener un ejemplo de cómo invalidar el evento OnPaint, vea Procedimiento para crear un control de Windows Forms que muestre el progreso.

Nota:

No invoque OnPaint directamente desde el control; en su lugar, invoque el método Invalidate (heredado de Control) o algún otro método que invoque Invalidate. A su vez, el método Invalidate invoca OnPaint. El método Invalidate está sobrecargado y, en función de los argumentos proporcionados a Invalidatee, un control vuelve a dibujar parte o toda su área de la pantalla.

La clase base Control define otro método que es útil para dibujar, el método OnPaintBackground.

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

OnPaintBackground representa el fondo (y, por tanto, la forma) de la ventana y se garantiza que es rápido, mientras que OnPaint representa los detalles y podría ser más lento, ya que las solicitudes de representación individuales se combinan en un evento Paint que cubre todas las áreas que deben volver a dibujarse. Es posible que quiera invocar el OnPaintBackground si, por ejemplo, desea dibujar un fondo de color degradado para el control.

Aunque OnPaintBackground tiene una nomenclatura similar a un evento y toma el mismo argumento que el método OnPaint, OnPaintBackground no es un verdadero método de evento. No hay ningún evento PaintBackground y OnPaintBackground no invoca los delegados de eventos. Al reemplazar el método OnPaintBackground, no es necesario que una clase derivada invoque el método OnPaintBackground de su clase base.

Conceptos básicos de GDI+

La clase Graphics proporciona métodos para dibujar diversas formas, como círculos, triángulos, arcos y elipses, así como métodos para mostrar texto. El espacio de nombres System.Drawing y sus subespacios contienen clases que encapsulan elementos gráficos como formas (círculos, rectángulos, arcos y otros), colores, fuentes, pinceles, etc. Para obtener más información sobre GDI, vea Uso de clases de gráficos administradas. Los aspectos básicos de GDI también se describen en Procedimiento para crear un control de Windows Forms que muestre el progreso.

Geometría de la región de dibujo

La propiedad ClientRectangle de un control especifica la región rectangular disponible para el control en la pantalla del usuario, mientras que la propiedad ClipRectangle de PaintEventArgs especifica el área de facto que se representa (recuerde que la representación se realiza en el método de evento Paint, que toma una instancia de PaintEventArgs como argumento). Es posible que un control solo tenga que pintar una parte de su área disponible, como sucede cuando se cambia una pequeña sección de la pantalla del control. En esas situaciones, un desarrollador de controles debe calcular el rectángulo real en el que dibujar y pasarlo a Invalidate. Las versiones sobrecargadas de Invalidate que toman un Rectangle o Region como argumento usan ese argumento para generar la propiedad ClipRectangle de PaintEventArgs.

En el fragmento de código siguiente se muestra cómo el control FlashTrackBar personalizado calcula el área rectangular en la que se va a dibujar. La variable client denota la propiedad ClipRectangle. Para obtener un ejemplo completo, vea Procedimiento para crear un control de Windows Forms que muestre el progreso.

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)

Liberación de recursos gráficos

Los objetos gráficos son caros porque usan recursos del sistema. Estos objetos incluyen instancias de la clase System.Drawing.Graphics, así como instancias de System.Drawing.Brush, de System.Drawing.Pen y otras clases de gráficos. Es importante crear un recurso gráfico solo cuando lo necesite y lo libere en cuanto haya terminado de usarlo. Si crea un tipo que implementa la interfaz IDisposable, llame a su método Dispose cuando haya terminado para liberar recursos.

En el fragmento de código siguiente se muestra cómo el control personalizado FlashTrackBar crea y libera un recurso Brush. Para obtener el código fuente completo, vea Procedimiento para crear un control de Windows Forms que muestre el progreso.

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

Consulte también