Como usar Imagens e Labels com transparência em .NET Windows

Se já tentou trabalhar com formulários mais complexos que incluíssem imagens e labels provavelmente deparou-se com o facto de que o Windows Forms não suporta verdadeiramente transparência. Provavelmente já arrancou bastantes cabelos a tentar - não desespere!

Mesmo que use o valor Transparent para a propriedade BackColor do controlo, não obterá transparência. O que acontece é que o controlo usará o fundo do seu "pai". Este comportamento é visível na próxima imagem.

Windows Forms doesn't support true transparency
Windows Forms não suporta verdadeiramente transparência

Neste artigo mostraremos uma forma simples de obter labels com imagens como fundo e também como podemos desenhar imagens e texto com a correcta transparência.

Como fazer labels transparentes

É fácil usar uma imagem como fundo e texto ou labels à frente com verdadeira transparência. Neste capítulo mostraremos como tornar o fundo de uma label transparente.

Podemos fazer com que uma label trate da transparência correctamente de 2 formas (existem mais, mas apenas falaremos das formas mais directas e simples):

  1. Usando um Panel, definindo a propriedade BackgroundImage e colocando Label(s) dentro do painel
  2. Definindo o pai da Label como sendo a PictureBox (label.Parent = pictureBox;)

Usaremos a primeira abordagem, visto que não requer nenhuma linha de código e podemos ver o resultado imediatamente no designer.

How to set transparent labels
Como definir transparência para as labels

Primeiro, começamos por arrastar um Panel para o formulário. Depois definimos a propriedade BackgroundImage para a imagem que queremos usar como fundo (podemos usar a propriedade BackgroundImageLayout para controlar o seu comportamento).

Por fim, adicionamos uma Label e definimos a propriedade BackColor com o valor Transparent (a primeira opção na divisão Web). O resultado deverá ser semelhante ao da imagem seguinte.

Label with transparency
Label com transparência

Isto permite-nos usar labels com transparência, mas as imagens continuam a não ter o resultado esperado (não há transparência nas imagens)! Não se preocupem, no próximo capítulo apresentaremos uma solução para usar imagens com verdadeira transparência.

Desenhar imagens com transparência usando GDI+

Desenhar imagens correctamente com transparência dá um pouco mais de trabalho, porque não podemos usar os controlos que vêm com o WindowsForms e .NET.

Para um uso mais complexo de imagens e gráficos podemos usar GDI+, que significa Graphics Device Interface (encontra-se no namespace System.Drawing).

O que vamos fazer é criar um controlo genérico que nos permitirá desenhar arbitrariamente imagens e texto. Isto pode ser encontrado no código fonte do projecto, mas se quiserem perceber como funciona e como o usar então continuem a ler.

Controlo genérico para desenhar imagens

Comecemos por criar uma nova classe descendente de Panel - chamemos-lhe DrawingArea. Esta classe terá um método abstracto OnDraw que será sobrecarregado pelas subclasses, por isso também precisamos de declarar a classe como abstracta.

Também adicionaremos um objecto Graphics onde terá lugar toda a actividade de desenho. Até este momento deveremos ter algo como o seguinte:

using System;
using System.Drawing;
using System.Windows.Forms;
using System.Drawing.Drawing2D;
 
/// <summary>
/// Use this for drawing custom graphics and text with transparency.
/// Inherit from DrawingArea and override the OnDraw method.
/// </summary>
abstract public class DrawingArea : Panel
{
    /// <summary>
    /// Drawing surface where graphics should be drawn.
    /// Use this member in the OnDraw method.
    /// </summary>
    protected Graphics graphics;
 
    /// <summary>
    /// Override this method in subclasses for drawing purposes.
    /// </summary>
    abstract protected void OnDraw();
}

Precisamos de nos certificar que a transparência do fundo do controlo será tratada correctamente. Para isto precisamos de sobrecarregar a propriedade CreateParams para que o estilo correcto seja incluido quando o controlo for instanciado (agradecimentos ao Bob Powell pela dica).

    protected override CreateParams CreateParams
    {
        get
        {
            CreateParams cp = base.CreateParams;
            cp.ExStyle |= 0x00000020; //WS_EX_TRANSPARENT
  
            return cp;
        }
    }

Precisamos ainda de mais duas coisas. Primeiro temos de nos certificar que o fundo não é desenhado. Fazemos isto sobrecarregando o método OnPaintBackground e deixando-o vazio.

A segunda coisa que precisamos é de sobrecarregar o método OnPaint. Isto permite-nos definir a rotina que será chamada quando for necessário desenhar o controlo.

    protected override void OnPaintBackground(PaintEventArgs pevent)
    {
        // Don't paint background
    }
  
    protected override void OnPaint(PaintEventArgs e)
    {
        // Update the private member so we can use it in the OnDraw method
        this.graphics = e.Graphics;
  
        // Set the best settings possible (quality-wise)
        this.graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;
        this.graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBilinear;
        this.graphics.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighQuality;
        this.graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
  
        // Calls the OnDraw subclass method
        OnDraw();
    }

Também defini um método DrawText e algumas variações para que fosse mais fácil escrever texto. Como são relativamente extensos deixei esses métodos fora do tutorial, mas podem encontrá-los no código fonte do projecto.

Usar o controlo para desenhar imagens e texto com transparência

Agora como podemos usar este controlo? Precisamos de criar uma subclasse de DrawingArea. Isto é bastante simples e fácil de fazer. Forneço aqui um exemplo:

class BroculosDrawing : DrawingArea
{
    protected override void OnDraw()
    {
        // Gets the image from the global resources
        Image broculoImage = global::WindowsApplication1.Properties.Resources.broculo;
  
        // Sets the images' sizes and positions
        int width = broculoImage.Size.Width;
        int height = broculoImage.Size.Height;
        Rectangle big = new Rectangle(0, 0, width, height);
        Rectangle small = new Rectangle(50, 50, (int)(0.75 * width), (int)(0.75 * height));
  
        // Draws the two images
        this.graphics.DrawImage(broculoImage, big);
        this.graphics.DrawImage(broculoImage, small);
  
        // Sets the text's font and style and draws it
        float fontSize = 8.25f;
        Point textPosition = new Point(50, 100);
        DrawText("http://www.broculos.net", "Microsoft Sans Serif", fontSize
            , FontStyle.Underline, Brushes.Blue, textPosition);
    }
}

Neste exemplo são desenhadas duas imagens e uma linha de texto (parecido com as imagens anteriores), mas agora com verdadeira transparência!

Podemos usar este controlo como um controlo normal. Compile a solução. Crie um novo formulário. O novo controlo deverá aparecer na toolbox. Arraste o controlo para o formulário e voila! Podem ver o resultado final na imagem seguinte.

Windows Forms com transparência completa em imagens e texto
Windows Forms com transparência completa em imagens e texto

Conclusão

Agora já sabem como desenhar imagens com transparência. A grande desvantagem é que não é tão fácil de usar como os controlos que vêm de "fábrica" com o Windows Forms. Os controlos por omissão são muito limitados para um uso mais avançado de imagem, por isso usamos GDI+ para ultrapassar isto.

Com este conhecimento e um pouco mais de trabalho deverá ser possível fazer uma TransparentPictureBox. Espero que tenha achado este artigo útil.

Recursos

Nuno Freitas
Publicado por Nuno Freitas em 12 março, 2008

Artigos relacionados