Pintando e desenhando em controles (Windows Forms .NET)

A pintura personalizada de controles é uma das muitas tarefas complicadas facilitadas pelo Windows Forms. Ao criar um controle personalizado, você tem muitas opções disponíveis para lidar com a aparência gráfica do controle. Se você estiver criando um controle personalizado, ou seja, um controle que herda do Control, deverá fornecer código para renderizar sua representação gráfica.

Importante

A documentação do Guia da Área de Trabalho para .NET 7 e .NET 6 está em construção.

Se você estiver criando um controle composto, que é um controle que herda de UserControl ou um dos controles existentes do Windows Forms, você pode substituir a representação gráfica padrão e fornecer seu próprio código gráfico.

Se você quiser fornecer renderização personalizada para um controle existente sem criar um novo controle, suas opções se tornarão mais limitadas. No entanto, ainda há uma ampla gama de possibilidades gráficas para seus controles e aplicativos.

Os elementos a seguir estão envolvidos na renderização de controles:

  • A funcionalidade de desenho fornecida pela classe System.Windows.Forms.Controlbase .
  • Os elementos essenciais da biblioteca gráfica GDI.
  • A geometria da região de desenho.
  • O procedimento para liberar recursos gráficos.

Desenho fornecido pelo controle

A classe Control base fornece funcionalidade de desenho por meio de seu Paint evento. Um controle aciona o Paint evento sempre que ele precisa atualizar sua exibição. Para obter mais informações sobre eventos no .NET, consulte Manipulando e gerando eventos.

A classe de dados do evento para o Paint evento, , PaintEventArgscontém os dados necessários para desenhar um controle - um identificador para um objeto gráfico e um retângulo que representa a região a ser desenhada.

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 é uma classe gerenciada que encapsula a funcionalidade de desenho, conforme descrito na discussão do GDI mais adiante neste artigo. O ClipRectangle é uma instância da Rectangle estrutura e define a área disponível na qual um controle pode desenhar. Um desenvolvedor de controle pode calcular o ClipRectangle uso ClipRectangle da propriedade de um controle, conforme descrito na discussão de geometria mais adiante neste artigo.

OnPaint

Um controle deve fornecer lógica de renderização substituindo o OnPaint método que ele herda do Control. OnPaint Obtém acesso a um objeto gráfico e a um retângulo para desenhar através das Graphics propriedades e da ClipRectanglePaintEventArgs instância passada para ele.

O código a seguir usa o System.Drawing namespace:

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

O OnPaint método da classe base Control não implementa nenhuma funcionalidade de desenho, mas apenas invoca os delegados de evento que estão registrados com o Paint evento. Ao substituir OnPainto , você normalmente deve invocar o OnPaint método da classe base para que os delegados registrados recebam o Paint evento. No entanto, os controles que pintam toda a superfície não devem invocar o , pois isso introduz a OnPaintcintilação.

Observação

Não invoque diretamente do seu controle, em vez disso, invoque o método (herdado de Control) ou algum outro método que invoque OnPaintInvalidate.Invalidate O Invalidate método, por sua vez, OnPaintinvoca . O Invalidate método é sobrecarregado e, dependendo dos argumentos fornecidos ao Invalidatee, redesenha parte ou toda a sua área de tela.

O código no OnPaint método do seu controle será executado quando o controle for desenhado pela primeira vez e sempre que for atualizado. Para garantir que o controle seja redesenhado sempre que ele for redimensionado, adicione a seguinte linha ao construtor do seu controle:

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

OnPaintBackground

A classe base Control define outro método que é útil para desenho, o OnPaintBackground método.

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

OnPaintBackground pinta o fundo (e, dessa forma, a forma) da janela e é garantido que seja rápido, enquanto OnPaint pinta os detalhes e pode ser mais lento porque as solicitações de pintura individuais são combinadas em um Paint evento que abrange todas as áreas que precisam ser redesenhadas. Talvez você queira invocar o OnPaintBackground se, por exemplo, você deseja desenhar um plano de fundo colorido de gradiente para seu controle.

Embora OnPaintBackground tenha uma nomenclatura semelhante a um evento e tenha o mesmo argumento que o OnPaint método, OnPaintBackground não é um método de evento verdadeiro. Não há nenhum PaintBackground evento e OnPaintBackground não invoca delegados de eventos. Ao substituir o método, uma classe derivada não é necessária para invocar o OnPaintBackgroundOnPaintBackground método de sua classe base.

Noções básicas sobre a GDI+

A Graphics classe fornece métodos para desenhar várias formas, como círculos, triângulos, arcos e elipses, e métodos para exibir texto. O System.Drawing namespace contém namespaces e classes que encapsulam elementos gráficos, como formas (círculos, retângulos, arcos e outros), cores, fontes, pincéis e assim por diante.

Geometria da região de desenho

A ClientRectangle propriedade de um controle especifica a região retangular disponível para o controle na tela do usuário, enquanto a propriedade de PaintEventArgs especifica a ClipRectangle área que é pintada. Um controle pode precisar pintar apenas uma parte da sua área disponível, como é o caso quando uma pequena seção da exibição do controle é alterada. Nessas situações, um desenvolvedor de controle deve calcular o retângulo real para desenhar e passar isso para Invalidate. As versões sobrecarregadas Invalidate que usam um ou Region como um Rectangle argumento usam esse argumento para gerar a ClipRectangle propriedade de PaintEventArgs.

Liberando recursos gráficos

Objetos gráficos são caros porque usam recursos do sistema. Esses objetos incluem instâncias da System.Drawing.Graphics classe e instâncias de , System.Drawing.Pene outras classes gráficasSystem.Drawing.Brush. É importante que você crie um recurso gráfico somente quando precisar dele e o libere assim que terminar de usá-lo. Se você criar uma instância de um tipo que implementa a IDisposable interface, chame seu Dispose método quando terminar de usá-lo para liberar recursos.

Confira também