C# Picturebox transparent background doesn't seem to work
Asked Answered
D

6

24

For a project of mine I need images to display with a transparent background. I made some .png images that have a transparent background(to check this I opened them in Photoshop). Now I have a class that extends PictureBox:

class Foo : PictureBox
{
    public Foo(int argument)
        : base()
    {
        Console.WriteLine(argument);//different in the real application of course.
        //MyProject.Properties.Resources.TRANSPARENCYTEST.MakeTransparent(MyProject.Properties.Resources.TRANSPARENCYTEST.GetPixel(1,1)); //<-- also tried this
        this.Image = MyProject.Properties.Resources.TRANSPARENCYTEST;
        ((Bitmap)this.Image).MakeTransparent(((Bitmap)this.Image).GetPixel(1, 1));
        this.SizeMode = PictureBoxSizeMode.StretchImage;
        this.BackColor = System.Drawing.Color.Transparent;
    }
}

this however just displays the picturebox with a white background, I just can't seem to make it work with a transparent background.

Denton answered 2/4, 2011 at 9:24 Comment(6)
Could you maybe provide a link to one of your images (the most basic one you have)?Swingletree
imgload.nl/upload/1301737511BLUE.png <-- this is one of the images I useDenton
I vaguely remember (from my WinForm days) that, when a control's background is marked "transparent", it is not really rendered transparent. Instead, it is rendered with the background color of the container. Is the background color of your container white?Councilman
@Stephen: I'm pretty sure that's what it is. In WinForms, there isn't any true transparency for images (not by default). If your controls overlap, you will see it.Swingletree
@Stepheb Then I'm beginning to hate C# more and more, no transparency lol? the only container it has is a form, with a white background indeed.Denton
What more could you expect than the form's (white) background showing through the transparent areas? I don't even understand what the question is here... Hans's answer is right on the money. (Also note that this has nothing to do with the C# language. C# doesn't have a user interface; you're using WinForms for that, which wraps the native Windows API. It wasn't really designed for transparency. You're not building web pages, but desktop applications.)Lindner
O
20

It probably works perfectly. You are seeing what's behind the picture box control. Which is the form. Whose BackColor is probably white. You can set the form's BackgroundImage property to be sure, you should see the image through the picture box. Like this:

enter image description here

Punching a hole through both the picture box and the form requires a bigger weapon, Form.TransparencyKey

Overhear answered 2/4, 2011 at 13:11 Comment(2)
This is not true transparency. If there is another control behind the picturebox it will not show through the transparent areas. Instead the form background will show over the control.Cantharides
Yes, yes, we know, it is a window and they are opaque by design. You see the Parent drawing itself as the background. You can get other controls to paint themselves as well but that is work. Use the Paint event or WPF to stack layers of paint on top of each other.Overhear
P
44

If you want to overlay images over images (and not images over form), this would make the trick:

overImage.Parent = backImage;
overImage.BackColor = Color.Transparent;
overImage.Location = thePointRelativeToTheBackImage;

Where overImage and backImage are PictureBox with png (with transparent background).

This is because, as said before, the transparency of an image is rendered using the back color of the Parent container. PictureBoxes haven't a "Parent" property so you have to make it manually (or create a cutom control of course).

Pause answered 6/2, 2012 at 10:40 Comment(3)
Just to clarify (wasn't sure about the type), I used overImage.Location = new Point(x,y) and every worked fine. Thanks a lot!Hardshell
Its working fine, point requires to define corresponding to parent container. As per this example backImage is parent container.Belshin
Setting .BackColor to Color.Transparent was the key. Thanks!Homochromatic
O
20

It probably works perfectly. You are seeing what's behind the picture box control. Which is the form. Whose BackColor is probably white. You can set the form's BackgroundImage property to be sure, you should see the image through the picture box. Like this:

enter image description here

Punching a hole through both the picture box and the form requires a bigger weapon, Form.TransparencyKey

Overhear answered 2/4, 2011 at 13:11 Comment(2)
This is not true transparency. If there is another control behind the picturebox it will not show through the transparent areas. Instead the form background will show over the control.Cantharides
Yes, yes, we know, it is a window and they are opaque by design. You see the Parent drawing itself as the background. You can get other controls to paint themselves as well but that is work. Use the Paint event or WPF to stack layers of paint on top of each other.Overhear
S
12

There's an excellent solution on the CodeProject website at

Making Transparent Controls - No Flickering

essentially the trick is to override the paintbackground event so as to loop through all the controls underlying the picturebox and redraw them. The function is:-

protected override void OnPaintBackground(PaintEventArgs e)
       // Paint background with underlying graphics from other controls
   {
       base.OnPaintBackground(e);
       Graphics g = e.Graphics;

       if (Parent != null)
       {
           // Take each control in turn
           int index = Parent.Controls.GetChildIndex(this);
           for (int i = Parent.Controls.Count - 1; i > index; i--)
           {
               Control c = Parent.Controls[i];

               // Check it's visible and overlaps this control
               if (c.Bounds.IntersectsWith(Bounds) && c.Visible)
               {
                   // Load appearance of underlying control and redraw it on this background
                   Bitmap bmp = new Bitmap(c.Width, c.Height, g);
                   c.DrawToBitmap(bmp, c.ClientRectangle);
                   g.TranslateTransform(c.Left - Left, c.Top - Top);
                   g.DrawImageUnscaled(bmp, Point.Empty);
                   g.TranslateTransform(Left - c.Left, Top - c.Top);
                   bmp.Dispose();
               }
           }
       }
   }
Spherulite answered 12/9, 2012 at 9:10 Comment(2)
That code project article has, unfortunately, been deleted as of 2 Oct 2013.Ladew
Still relevant today, this article... Can't wait to get the OK from the boss to switch to wpf :(Ardeha
R
4

I know your Question is founded in C#, but due to the similarity, & ease of conversion from VB.NET, I'll add a full VB version that also allows updating of the control's background as you move it around.

You already have an answer, but this is for others who find this post by search engines, & would like a VB version, or simply want to find a FULL convertible sample if they also need it in C#.

Create a new Custom Control Class, & paste the following into it... overwriting the default class stuff:

Custom Control Class:

Public Class TransparentPictureBox
    Private WithEvents refresher As Timer
    Private _image As Image = Nothing

    Public Sub New()

        ' This call is required by the designer.
        InitializeComponent()

        ' Add any initialization after the InitializeComponent() call.
        refresher = New Timer()
        'refresher.Tick += New EventHandler(AddressOf Me.TimerOnTick)
        refresher.Interval = 50
        refresher.Start()

    End Sub

    Protected Overrides ReadOnly Property CreateParams() As CreateParams
        Get
            Dim cp As CreateParams = MyBase.CreateParams
            cp.ExStyle = cp.ExStyle Or &H20
            Return cp
        End Get
    End Property

    Protected Overrides Sub OnMove(ByVal e As EventArgs)
        MyBase.OnMove(e)
        MyBase.RecreateHandle()
    End Sub

    Protected Overrides Sub OnPaint(ByVal e As System.Windows.Forms.PaintEventArgs)
        MyBase.OnPaint(e)

        'Add your custom paint code here
        If _image IsNot Nothing Then
            e.Graphics.DrawImage(_image, CInt(Width / 2) - CInt(_image.Width / 2), CInt(Height / 2) - CInt(_image.Height / 2))
        End If
    End Sub


    Protected Overrides Sub OnPaintBackground(ByVal e As System.Windows.Forms.PaintEventArgs)
        ' Paint background with underlying graphics from other controls
        MyBase.OnPaintBackground(e)
        Dim g As Graphics = e.Graphics

        If Parent IsNot Nothing Then
            ' Take each control in turn
            Dim index As Integer = Parent.Controls.GetChildIndex(Me)
            For i As Integer = Parent.Controls.Count - 1 To index + 1 Step -1
                Dim c As Control = Parent.Controls(i)

                ' Check it's visible and overlaps this control
                If c.Bounds.IntersectsWith(Bounds) AndAlso c.Visible Then
                    ' Load appearance of underlying control and redraw it on this background
                    Dim bmp As New Bitmap(c.Width, c.Height, g)
                    c.DrawToBitmap(bmp, c.ClientRectangle)
                    g.TranslateTransform(c.Left - Left, c.Top - Top)
                    g.DrawImageUnscaled(bmp, Point.Empty)
                    g.TranslateTransform(Left - c.Left, Top - c.Top)
                    bmp.Dispose()
                End If
            Next
        End If
    End Sub

    Public Property Image() As Image
        Get
            Return _image
        End Get
        Set(value As Image)
            _image = value
            MyBase.RecreateHandle()
        End Set
    End Property

    Private Sub refresher_Tick(sender As Object, e As System.EventArgs) Handles refresher.Tick
        MyBase.RecreateHandle()
        refresher.Stop()
    End Sub
End Class

...save the class, then clean your project, & build again. The new control should appear as a new tool Item. Find it, & drag it to your form.

I have had issues with this control though... It happens when I try to load an animated "Loading" .gif image.

The image does not animate, & also has display problems when you hide the control, then try to display it again.

Sort those issues out, & you'll have a perfect Custom Control Class. :)

EDIT:

I have no idea if the following will work in C#'s IDE or not, but here's my attempt at conversion:

using Microsoft.VisualBasic;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
public class TransparentPictureBox
{
    private Timer withEventsField_refresher;
    private Timer refresher {
        get { return withEventsField_refresher; }
        set {
            if (withEventsField_refresher != null) {
                withEventsField_refresher.Tick -= refresher_Tick;
            }
            withEventsField_refresher = value;
            if (withEventsField_refresher != null) {
                withEventsField_refresher.Tick += refresher_Tick;
            }
        }
    }

    private Image _image = null;

    public TransparentPictureBox()
    {
        // This call is required by the designer.
        InitializeComponent();

        // Add any initialization after the InitializeComponent() call.
        refresher = new Timer();
        //refresher.Tick += New EventHandler(AddressOf Me.TimerOnTick)
        refresher.Interval = 50;
        refresher.Start();

    }

    protected override CreateParams CreateParams {
        get {
            CreateParams cp = base.CreateParams;
            cp.ExStyle = cp.ExStyle | 0x20;
            return cp;
        }
    }

    protected override void OnMove(EventArgs e)
    {
        base.OnMove(e);
        base.RecreateHandle();
    }

    protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
    {
        base.OnPaint(e);

        //Add your custom paint code here
        if (_image != null) {
            e.Graphics.DrawImage(_image, Convert.ToInt32(Width / 2) - Convert.ToInt32(_image.Width / 2), Convert.ToInt32(Height / 2) - Convert.ToInt32(_image.Height / 2));
        }
    }


    protected override void OnPaintBackground(System.Windows.Forms.PaintEventArgs e)
    {
        // Paint background with underlying graphics from other controls
        base.OnPaintBackground(e);
        Graphics g = e.Graphics;

        if (Parent != null) {
            // Take each control in turn
            int index = Parent.Controls.GetChildIndex(this);
            for (int i = Parent.Controls.Count - 1; i >= index + 1; i += -1) {
                Control c = Parent.Controls(i);

                // Check it's visible and overlaps this control
                if (c.Bounds.IntersectsWith(Bounds) && c.Visible) {
                    // Load appearance of underlying control and redraw it on this background
                    Bitmap bmp = new Bitmap(c.Width, c.Height, g);
                    c.DrawToBitmap(bmp, c.ClientRectangle);
                    g.TranslateTransform(c.Left - Left, c.Top - Top);
                    g.DrawImageUnscaled(bmp, Point.Empty);
                    g.TranslateTransform(Left - c.Left, Top - c.Top);
                    bmp.Dispose();
                }
            }
        }
    }

    public Image Image {
        get { return _image; }
        set {
            _image = value;
            base.RecreateHandle();
        }
    }

    private void refresher_Tick(object sender, System.EventArgs e)
    {
        base.RecreateHandle();
        refresher.Stop();
    }
}

Try it out, & see for yourself i guess :P

ps: I'm not a guru, so expect all kinds of mistakes in both the C#, & VB.NET versions. lol

Roid answered 15/10, 2013 at 13:42 Comment(0)
G
3

If you are displaying png with transparence in picture box, it will be automatically take transparence into account, so you have no need to set transparent color

Gezira answered 2/4, 2011 at 9:44 Comment(1)
I just tryed out you image with picturebox. Its work correctly without any transparence specification.Gezira
S
1

The above answers seem to solve your problem. You are indeed seeing what's behind the picture box control - the form itself with backColor white. I have here created a simple function that first converts an image of byte type (array) into a bitmap and thereafter setting specific colors (from the bitmap pic) to transparent. Something you might as well use:

 using System;
   using System.Drawing;
   using System.Drawing.Imaging;
   using System.Windows.Forms;
    public void LogoDrawTransparent(PaintEventArgs e)
    {
        // Create a Bitmap object from an image file.
        Image myImg;
        Bitmap myBitmap;

        try
        {
            myImg = cls_convertImagesByte.GetImageFromByte(newImg);
            myBitmap = new Bitmap(myImg); // @"C:\Temp\imgSwacaa.jpg");  

            // Get the color of a background pixel.
            Color backColor = myBitmap.GetPixel(0, 0); // GetPixel(1, 1); 
            Color backColorGray = Color.Gray;
            Color backColorGrayLight = Color.LightGray;
            Color backColorWhiteSmoke = Color.WhiteSmoke;
            Color backColorWhite = Color.White;
            Color backColorWheat = Color.Wheat;

            // Make backColor transparent for myBitmap.
            myBitmap.MakeTransparent(backColor);
                    // OPTIONALLY, you may make any other "suspicious" back color transparent (usually gray, light gray or whitesmoke)
            myBitmap.MakeTransparent(backColorGray);
            myBitmap.MakeTransparent(backColorGrayLight);
            myBitmap.MakeTransparent(backColorWhiteSmoke);

            // Draw myBitmap to the screen.
            e.Graphics.DrawImage(myBitmap, 0, 0, pictureBox1.Width, pictureBox1.Height); //myBitmap.Width, myBitmap.Height);
        }
        catch
        {
            try { pictureBox1.Image = cls_convertImagesByte.GetImageFromByte(newImg); }
            catch { } //must do something
        }
    }

You may fire this func on Paint of the pictureBox. This is my class that is referenced n the function above:

    class cls_convertImagesByte
{

    public static Image GetImageFromByte(byte[] byteArrayIn)
    {
        MemoryStream ms = new MemoryStream(byteArrayIn);
        Image returnImage = Image.FromStream(ms);
        return returnImage;
    }

    public static byte[] GetByteArrayFromImage(System.Drawing.Image imageIn)
    {
        MemoryStream ms = new MemoryStream();
        imageIn.Save(ms, System.Drawing.Imaging.ImageFormat.Gif);
        return ms.ToArray();
    }
}

Thanks. Chagbert

Sruti answered 23/5, 2014 at 6:10 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.