Representar un control de formularios Windows Forms

Actualización: noviembre 2007

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

Los siguientes elementos intervienen en la representación de los controles:

  • La funcionalidad de dibujo proporcionada por la clase base System.Windows.Forms.Control.

  • Los elementos básicos de la biblioteca gráfica GDI.

  • La geometría de la zona de dibujo.

  • El procedimiento para liberar recursos gráficos.

Funcionalidad de dibujo proporcionada por un control

La clase base Control proporciona funcionalidad de dibujo a través del evento Paint. Un control provoca el evento Paint cada vez que necesita actualizar su apariencia. Para obtener más información acerca de los eventos de .NET Framework, vea Controlar y provocar eventos.

La clase de datos del evento Paint, PaintEventArgs, guarda los datos necesarios para dibujar un control; un identificador de un objeto Graphics y un objeto Rectangle que representa la zona 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, como se indica en la descripción de GDI más adelante en este mismo tema. ClipRectangle es una instancia de la estructura Rectangle y define el área disponible en la que puede dibujar un control. Los desarrolladores de controles pueden calcular la propiedad ClipRectangle utilizando la propiedad ClipRectangle de un control, como se explica en la descripción de geometría más adelante en este mismo tema.

El control debe proporcionar lógica de representación al reemplazar el método OnPaint que hereda de Control. El método OnPaint obtiene acceso al objeto gráfico y a un rectángulo donde se va a dibujar mediante las propiedades Graphics y ClipRectangle de la instancia 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 se limita a invocar los delegados de evento registrados en el evento Paint. Cuando se reemplaza OnPaint, normalmente se invoca el método OnPaint de la clase base para que los delegados registrados reciban el evento Paint. Sin embargo, los controles que dibujan toda su superficie no deben invocar el método OnPaint de la clase base, ya que esto causa parpadeos. Para obtener un ejemplo del reemplazo del evento OnPaint, vea Cómo: Crear un control de formularios Windows Forms que muestre el progreso.

Nota:

No invoque OnPaint directamente desde el control, invoque en su lugar el método Invalidate (heredado de Control) o algún otro método que invoque Invalidate. El método Invalidate a su vez invoca OnPaint. El método Invalidate está sobrecargado y, dependiendo de los argumentos que se suministren a Invalidate e, un control volverá a dibujar todo el área de la pantalla o parte de ésta.

La clase base Control define otro método que resulta de utilidad para dibujar, el método OnPaintBackground.

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

OnPaintBackground dibuja el fondo (y por tanto la forma) de la ventana y está garantizado como rápido, mientras que OnPaint dibuja los detalles y posiblemente sea más lento, ya que las distintas solicitudes de dibujo se combinan en un solo evento Paint que abarca todas las áreas que han de volverse a dibujar. Se puede invocar el método OnPaintBackground si, por ejemplo, se va a dibujar un fondo en color degradado para el control.

Aunque OnPaintBackground tiene una nomenclatura similar a la de los eventos y acepta los mismos argumentos que el método OnPaint, OnPaintBackground no es un método de evento auténtico. No hay ningún evento PaintBackground y OnPaintBackground no invoca delegados de eventos. Al reemplazar el método OnPaintBackground, no se requiere una clase derivada para invocar el método OnPaintBackground de su clase base.

Funciones básicas de GDI+

La clase Graphics proporciona métodos para dibujar varias formas como círculos, triángulos, arcos y elipses, y métodos para mostrar texto. El espacio de nombres System.Drawing y sus subespacios de nombres contienen clases que encapsulan elementos gráficos, como formar (círculos, rectángulos, arcos y otras), colores, fuentes, pinceles, etc. Para obtener más información acerca de GDI, vea Utilizar clases gráficas administradas. La información esencial de GDI también se describe en Cómo: Crear un control de formularios Windows Forms que muestre el progreso.

La geometría de la zona de dibujo.

La propiedad ClientRectangle de un control especifica la zona rectangular disponible para el control en la pantalla del usuario, mientras que la propiedad ClipRectangle de PaintEventArgs especifica la zona dibujada en realidad. (Conviene recordar que el dibujo se realiza en el método Paint que toma una instancia PaintEventArgs como su argumento.) Es posible que los controles sólo tengan que dibujar una parte del área disponible, como sucede cuando varía una pequeña sección de la apariencia del control. En tales circunstancias, un desarrollador de controles deberá calcular el rectángulo real en el que se va a dibujar y pasarlo a Invalidate. Las versiones sobrecargadas del método Invalidate que toma Rectangle o Region como argumento utilizan ese argumento para generar la propiedad ClipRectangle de PaintEventArgs.

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

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);

Liberar recursos gráficos

Los objetos gráficos son costosos ya que utilizan recursos del sistema. Tales objetos incluyen instancias de la clase System.Drawing.Graphics así como crea instancias de System.Drawing.Brush, System.Drawing.Pen y otras clases de gráficos. Es importante que los recursos gráficos se creen sólo cuando sean necesarios y se liberen tan pronto como se terminen de usar. Si se crea un tipo que implementa la interfaz IDisposable, se puede llamar al método Dispose una vez se haya terminado de utilizar y así liberar recursos.

En el siguiente fragmento de código se muestra cómo el control personalizado FlashTrackBar crea y libera un recurso Brush. Para el código fuente completo, vea Cómo: Crear un control de formularios Windows Forms que muestre el progreso.

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 (BackgroundImage IsNot 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 (baseBackground IsNot 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;
    }
}

Vea también

Tareas

Cómo: Crear un control de formularios Windows Forms que muestre el progreso