Renderizando um controle dos Windows Forms

Renderização se refere ao processo de criar uma representação visual na tela do usuário. O Windows Forms usa GDI (a nova biblioteca de gráficos do Windows) para renderização. As classes gerenciadas que fornecem acesso ao GDI estão no System.Drawing namespace e em seus subnamespaces.

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.

Funcionalidade de desenho fornecida pelo controle

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

A classe de dados de evento para o Paint evento, PaintEventArgs, contém os dados necessários para desenhar um controle — um identificador para um objeto gráfico e um objeto retangular que representa a região a ser desenhada. Esses objetos são mostrados em negrito no seguinte 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 é uma classe gerenciada que encapsula a funcionalidade de desenho, conforme descrito na discussão sobre GDI mais adiante neste tópico. 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 usando a ClipRectangle propriedade de um controle, conforme descrito na discussão sobre geometria mais adiante neste tópico.

Um controle deve fornecer lógica de renderização substituindo o OnPaint método que ele herda de Control. OnPaint obtém acesso a um objeto gráfico e um retângulo para desenhar por meio do Graphics e as ClipRectangle propriedades da PaintEventArgs instância passadas para ele.

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

O OnPaint método da classe base Control não implementa nenhuma funcionalidade de desenho, mas apenas invoca os delegados de evento 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 , da classe OnPaintbase, pois isso introduz cintilação. Para obter um exemplo de substituição do OnPaint evento, consulte Como criar um controle do Windows Forms que mostre o progresso.

Observação

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

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

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

OnPaintBackground pinta o fundo (e, portanto, a forma) da janela e é garantido que será 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 cobre todas as áreas que precisam ser redesenhadas. Talvez você queira invocar o OnPaintBackground se, por exemplo, quiser desenhar um plano de fundo colorido de gradiente para seu controle.

Embora OnPaintBackground tenha uma nomenclatura semelhante a um evento e use 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 evento. Ao substituir o OnPaintBackground método, uma classe derivada não é necessária para invocar o OnPaintBackground 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, bem como métodos para exibir texto. O System.Drawing namespace e seus subnamespaces contêm classes que encapsulam elementos gráficos, como formas (círculos, retângulos, arcos e outros), cores, fontes, pincéis e assim por diante. Para obter mais informações sobre GDI, consulte Usando classes de gráficos gerenciados. Os fundamentos do GDI também são descritos em Como criar um controle do Windows Forms que mostra o progresso.

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 ClipRectangle propriedade de PaintEventArgs especifica a área que é realmente pintada. (Lembre-se de que a Paint pintura é feita no método de evento que usa uma PaintEventArgs instância como argumento). 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 passá-lo para Invalidate. As versões sobrecarregadas de Invalidate que usam um Rectangle ou Region como um argumento usam esse argumento para gerar a ClipRectangle propriedade de PaintEventArgs.

O fragmento de código a seguir mostra como o controle personalizado FlashTrackBar calcula a área retangular na qual desenhar. A client variável denota a ClipRectangle propriedade. Para ver um exemplo completo, consulte Como criar um controle do Windows Forms que mostre o progresso.

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)

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, bem como instâncias de System.Drawing.Brush, System.Drawing.Pen, e outras classes gráficas. É importante que você crie um recurso de gráfico somente quando precisar dele e o libere assim que terminar de usá-lo. Se você criar um tipo que implemente a IDisposable interface, chame seu Dispose método quando terminar de usá-lo para liberar recursos.

O fragmento de código a seguir mostra como o FlashTrackBar controle personalizado cria e libera um Brush recurso. Para ver o código-fonte completo, consulte Como criar um controle do Windows Forms que mostre o progresso.

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

Confira também