Procesar un control de formularios Windows Forms

Por procesamiento 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 el procesamiento. 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 el procesamiento 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 System.Windows.Forms.Control proporciona la funcionalidad de dibujo a través de su 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
[C#]
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 clase Rectangle y define el área disponible en la que puede dibujar un control. Los programadores de controles pueden calcular el ClipRectangle utilizando la propiedad ClientRectangle de un control, como se explica en la descripción de geometría más adelante en este mismo tema.

Los controles deben proporcionar lógica de procesamiento reemplazando el método OnPaint que heredan de Control. OnPaint tiene acceso a un objeto Graphics y a un rectángulo para dibujar a través de las propiedades Graphics y ClipRectangle de la instancia PaintEventArgs que le fue pasada.

Protected Overridable Sub OnPaint(pe As PaintEventArgs)
[C#]
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 de cómo reemplazar el evento Onpaint, vea Ejemplo de control de formularios Windows Forms.

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, 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)
[C#]
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. OnPaintBackground se puede invocar 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 System.Drawing.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 sobre GDI+, vea Dibujar y editar imágenes. Los conceptos esenciales de GDI+ también se describen en el Tutorial de formularios Windows Forms.

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 PaintEvent 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 programador de controles deberá calcular el rectángulo real en el que se va a dibujar y pasarlo a Invalidate. Las versiones sobrecargadas de Invalidate, que toman Rectangle o Region como argumento, utilizan dicho 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 ClientRectangle. Para obtener un ejemplo completo, vea Ejemplo de control de formularios Windows Forms.

Dim invalid As New Rectangle(client.X + min, client.Y, max - min, client.Height)
Invalidate(invalid)
[C#]
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. Entre estos objetos se encuentran instancias de la clase System.Drawing.Graphics e instancias de System.Drawing.Brush, System.Drawing.Pen, así como otras clases gráficas. 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 obtener el código fuente completo, vea Ejemplo de control de formularios Windows Forms.

Private baseBackground As Brush = Nothing
Protected Overrides Sub OnPaint(e As PaintEventArgs)
   MyBase.OnPaint(e)
   If baseBackground Is Nothing Then
      ...
      baseBackground = New SolidBrush(BackColor)
   End If
   ...
End Sub

Protected Overrides Sub OnResize(e As EventArgs)
   MyBase.OnResize(e)
   If Not (baseBackground Is Nothing) Then
      baseBackground.Dispose()
      baseBackground = Nothing
   End If
End Sub
[C#]
private Brush baseBackground = null;
protected override void OnPaint(PaintEventArgs e) {
   base.OnPaint(e);
   if (baseBackground == null) {
      ...
      baseBackground = new SolidBrush(BackColor);
   }
   ...
}

protected override void OnResize(EventArgs e) {
   base.OnResize(e);
   if (baseBackground != null) {
      baseBackground.Dispose();
      baseBackground = null;
   }
}

Vea también

Ejemplo de control de formularios Windows Forms