Pintura y dibujo de controles (Windows Forms para .NET)

La pintura personalizada de controles es una de las muchas tareas complicadas que Windows Forms simplifica. Cuando crea un control personalizado, tiene muchas opciones disponibles para controlar la apariencia gráfica del control. Si va a crear un control personalizado, es decir, un control que hereda de Control, debe proporcionar código para representar su representación gráfica.

Importante

La documentación de la guía de escritorio para .NET 7 y .NET 6 está en proceso de elaboración.

Si va a crear un control compuesto, que es un control que hereda de UserControl o de uno de los controles de Windows Forms existentes, puede reemplazar la representación gráfica estándar y proporcionar código gráfico propio.

Si quiere proporcionar una representación personalizada de un control existente sin crear otro control, las opciones se vuelven más limitadas. Sin embargo, todavía hay una amplia gama de posibilidades gráficas para los controles y las aplicaciones.

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.

Dibujo proporcionado 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 más información sobre los eventos en .NET, consulte Control y generación de eventos.

La clase de datos del evento Paint, PaintEventArgs, contiene los datos necesarios para dibujar un control: un manipulador para un objeto gráfico y un rectángulo que representa la región en la que se va a dibujar.

public class PaintEventArgs : EventArgs, IDisposable
{

    public System.Drawing.Rectangle ClipRectangle {get;}
    public System.Drawing.Graphics Graphics {get;}

    // Other properties and methods.
}
Public Class PaintEventArgs
    Inherits EventArgs
    Implements IDisposable

    Public ReadOnly Property ClipRectangle As System.Drawing.Rectangle
    Public ReadOnly Property Graphics As System.Drawing.Graphics

    ' Other properties and methods.
End Class

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 artículo. 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 artículo.

OnPaint

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.

El siguiente código usa el espacio de nombres System.Drawing:

protected override void OnPaint(PaintEventArgs e)
{
    // Call the OnPaint method of the base class.
    base.OnPaint(e);

    // Declare and instantiate a new pen that will be disposed of at the end of the method.
    using var myPen = new Pen(Color.Aqua);

    // Create a rectangle that represents the size of the control, minus 1 pixel.
    var area = new Rectangle(new Point(0, 0), new Size(this.Size.Width - 1, this.Size.Height - 1));

    // Draw an aqua rectangle in the rectangle represented by the control.
    e.Graphics.DrawRectangle(myPen, area);
}
Protected Overrides Sub OnPaint(e As PaintEventArgs)
    MyBase.OnPaint(e)

    ' Declare and instantiate a drawing pen.
    Using myPen = New System.Drawing.Pen(Color.Aqua)

        ' Create a rectangle that represents the size of the control, minus 1 pixel.
        Dim area = New Rectangle(New Point(0, 0), New Size(Me.Size.Width - 1, Me.Size.Height - 1))

        ' Draw an aqua rectangle in the rectangle represented by the control.
        e.Graphics.DrawRectangle(myPen, area)

    End Using
End Sub

El método OnPaint de la clase Control base 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 pintan toda la superficie no deben invocar el OnPaint de la clase base, ya que este introduce parpadeo.

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 para e de Invalidate, vuelve a dibujar parte o toda su área de la pantalla.

El código del método OnPaint del control se ejecutará cuando se dibuje el control por primera vez y siempre que se actualice. Para asegurarse de que el control se vuelve a dibujar cada vez que cambia de tamaño, agregue la siguiente línea al constructor del control:

SetStyle(ControlStyles.ResizeRedraw, true);
SetStyle(ControlStyles.ResizeRedraw, True)

OnPaintBackground

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

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

OnPaintBackground pinta el fondo (y de este modo, la forma) de la ventana y se garantiza que es rápido, mientras que OnPaint pinta los detalles y podría ser más lento, ya que las solicitudes de pintura 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, y métodos para mostrar texto. El System.Drawingespacio de nombres contiene espacios de nombres y clases que encapsulan elementos gráficos como formas (círculos, rectángulos, arcos y otros), colores, fuentes, pinceles, etc.

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 que se pinta. 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.

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 e instancias de System.Drawing.Brush, System.Drawing.Pen y otras clases de gráficos. Es importante que cree un recurso de gráficos solo cuando lo necesite y lo libere en cuanto haya terminado de usarlo. Si crea una instancia de un tipo que implementa la interfaz IDisposable, llame a su método Dispose cuando haya terminado de liberar recursos.

Vea también