Transparent Graphic Background

B

Brian Ward

I am looking for a simple way to set the image transparency in a PictureBox.
I have a moving PictureBox containing a graphic image .. moving by
incrementing its Left property.
The background however shows white as the PictureBox moves but I want it
to be transparent.
The PictureBox BackColor is set to Transparent .. but no affect. I have
used PhotoShop to make the image background transparent .. again no
affect.
If I make the image a BackgroundImage to a Panel instead of a PictureBox
it shows up as I want it .. but then I can't move the Panel.
I am using C# Visual Express 2005.
Any help much appreciated.
===
Brian
===
 
P

Peter Duniho

Brian said:
I am looking for a simple way to set the image transparency in a PictureBox.
I have a moving PictureBox containing a graphic image .. moving by
incrementing its Left property.
The background however shows white as the PictureBox moves but I want it
to be transparent.
The PictureBox BackColor is set to Transparent .. but no affect. I have
used PhotoShop to make the image background transparent .. again no
affect.

What format did you save the image in? JPEG doesn't support
transparency. TIFF and PNG do, though.

Also, how do you set the picture for the PictureBox? I have used PNG
files with transparency, setting the Image property of the PictureBox to
the image I want (in my case, set in the Designer, but loading at
run-time should work fine too).

It should work. If the above does not help you fix the problem, you
should be more specific about what you're doing.

Pete
 
B

Brian Ward

The message <[email protected]>
from Peter Duniho said:
What format did you save the image in? JPEG doesn't support
transparency. TIFF and PNG do, though.

GIF format.
I thought that was supposed to work but I'll try the others.
Also, how do you set the picture for the PictureBox? I have used PNG
files with transparency, setting the Image property of the PictureBox to
the image I want (in my case, set in the Designer, but loading at
run-time should work fine too).

In Form Designer Mode I access the PictureBox Image property and browse
to the image file.
It should work. If the above does not help you fix the problem, you
should be more specific about what you're doing.
Pete

Well its just using a timer_Tick() event to move the PictureBox across
the screen by increasing the Left property. At the moment I have to set
the Form background to white so the PictureBox background doesn't show
... but thats not what I want.
Thanks .. I'll try PNG and TIFF
==
Brian
==
 
P

Peter Duniho

Brian said:
GIF format.
I thought that was supposed to work but I'll try the others.

GIF should work too. At least, I know it works in .NET 2.0, and since
you're using VS 2005 Express, I assume that you're using .NET 2.0 or later.
[...]
Well its just using a timer_Tick() event to move the PictureBox across
the screen by increasing the Left property. At the moment I have to set
the Form background to white so the PictureBox background doesn't show
... but thats not what I want.
Thanks .. I'll try PNG and TIFF

As an initial suggestion: don't mess with the animation until you get
the GIF drawing statically correctly. It just confuses the issue.

You should double-check to make sure that the BackColor of the
PictureBox control is set to Transparent as you believe it is. Assuming
it is set correctly, then the only thing I can think of is that the GIF
isn't actually being saved out with transparency. One way to check that
would be to make a simple HTML page with a non-white background color
and let your browser display it:

<body style="background-color: #008">
<img src="Test.gif">
</body>

(yes, that's ill-formed HTML...but I think most browsers will still
display it fine :) )

Of course, you could also just load it back into the image editing
program and verify that the transparency is still there.

The bottom line here is that what you're doing should work fine. If
none of the above still leads to a solution, you should probably post a
complete-but-concise sample of code that demonstrates the problem. You
may also want to provide the actual GIF file, so that your entire
scenario can be tested by other people.

(Don't attach the GIF file to your post; this isn't a binary newsgroup
so it'd be bad form anyway, and because it's not many ISP's will either
block the entire post or just strip off the attachment anyway. You can
use upload/download web sites such as http://www.filecrunch.com/,
http://www.sendspace.com/, http://www.yousendit.com/, etc. which allow
you to upload a file and then get a link that you can include in your
message so others can download it).

Pete
 
B

Brian Ward

Thanks Peter
=============
I have made some progress through double-checking etc.
I thought I had saved the transparency from Photoshop but apparently
not! So I now have a transparent graphic with the form background
colour showing through instead of white as before. So far so good.
But ... when the PictureBox moves it appears to take the form background
colour with it .. i.e. when the box moves across other objects on the
form such as panels, buttons, other PictureBoxes, etc. the outline of
the Picturebox then shows up again .. with the form colour as its
background. I would really like the graphic image only to be seen (not
its container) as it passes other objects.
===
Brian
===


The message <[email protected]>
GIF should work too. At least, I know it works in .NET 2.0, and since
you're using VS 2005 Express, I assume that you're using .NET 2.0 or later.
[...]
Well its just using a timer_Tick() event to move the PictureBox across
the screen by increasing the Left property. At the moment I have to set
the Form background to white so the PictureBox background doesn't show
... but thats not what I want.
Thanks .. I'll try PNG and TIFF
As an initial suggestion: don't mess with the animation until you get
the GIF drawing statically correctly. It just confuses the issue.
You should double-check to make sure that the BackColor of the
PictureBox control is set to Transparent as you believe it is. Assuming
it is set correctly, then the only thing I can think of is that the GIF
isn't actually being saved out with transparency. One way to check that
would be to make a simple HTML page with a non-white background color
and let your browser display it:
<body style="background-color: #008">
<img src="Test.gif">
</body>
(yes, that's ill-formed HTML...but I think most browsers will still
display it fine :) )
Of course, you could also just load it back into the image editing
program and verify that the transparency is still there.
The bottom line here is that what you're doing should work fine. If
none of the above still leads to a solution, you should probably post a
complete-but-concise sample of code that demonstrates the problem. You
may also want to provide the actual GIF file, so that your entire
scenario can be tested by other people.
 
P

Peter Duniho

Brian said:
Thanks Peter
=============
I have made some progress through double-checking etc.
I thought I had saved the transparency from Photoshop but apparently
not! So I now have a transparent graphic with the form background
colour showing through instead of white as before. So far so good.
But ... when the PictureBox moves it appears to take the form background
colour with it .. i.e. when the box moves across other objects on the
form such as panels, buttons, other PictureBoxes, etc. the outline of
the Picturebox then shows up again .. with the form colour as its
background. I would really like the graphic image only to be seen (not
its container) as it passes other objects.

Hmph. I found this:

Windows Forms controls do not support true transparency. The
background of a transparent Windows Forms control is painted
by its parent.

(http://msdn2.microsoft.com/en-us/library/wk5b13s4.aspx)

I was able to reproduce the behavior you're reporting, and I admit it
surprised me. But then I found the above quote, which suggests to me
that there's no straight-forward way to do what you want.

I tried some things involving creating a PictureBox-derived class. I
thought maybe I could set the AllPaintingInWmPaint style and not doing
anything in the OnPaintBackground method, but in spite of the
implication in the docs that you can set the style and avoid having to
paint the background, Windows apparently expects that you will
completely draw the entire control when this style is set. Simply not
drawing the part of the control that's transparent results in garbage,
presumably left over from temporary caching of drawing of the other
controls in the form (at least that's what it looks like to me).

Ordinarily, I would suggest skipping making the thing a control and just
drawing it directly yourself into the form. However, it sounds as
though you want it to be drawn on top of other controls, and of course
all of the form's own rendering happens under any controls that are in
the form. So that wouldn't be appropriate in your case.

If you can come up with a way to represent the transparency as a Region
(or rather, the area that's not transparent), then you could use that
Region as the Control's region, which would cause all drawing, including
the background, to be clipped to the region. But if you're using a
plain bitmap, that could be problematic, as I'm not aware of any
built-in functions that would do that. I suppose you could do it
yourself by scanning the bitmap line by line, checking the alpha of each
pixel, generating a Region that way (either pixel-by-pixel, or
consolidating adjacent non-transparent pixels in each line into a single
rectangle you add to the Region). But that seems like a lot of work to
me. :)

Unfortunately, that's all I have with respect to decent ideas. So,
here's one last bad one: write your own picture-box control that copies
whatever's underneath it for use in drawing it's own background. I
tried it, and it does work. There's a little bit of "smudging" that
goes on as the control is moved, because some how it gets drawn in its
new location before the OnLocationChanged method is called. This might
be fixable, but I think I'll leave that up to you to explore, at least
for now. :)

I think this is a really hacky solution, and I'd probably try to use
something a little more "polite". Also, this has potential performance
issues, since each time the control is moved, the entire form has to be
redrawn into a bitmap. It's plenty fast on my fast PC with a small form
though.

Anyway, see if this is helpful at all (take this code and use it to
implement a custom control class in your own project):

using System;
using System.Drawing;
using System.Windows.Forms;

namespace TestTransparentGIF
{
public partial class TransparentPictureBox : PictureBox
{
Bitmap _bmpBackground;
bool _fDontDraw;

public TransparentPictureBox()
{
InitializeComponent();

SetStyle(ControlStyles.Opaque, true);
}

protected override void OnPaint(PaintEventArgs pe)
{
if (!_fDontDraw)
{
if (_bmpBackground == null)
{
using (Bitmap bmpT = new Bitmap(Parent.Size.Width,
Parent.Size.Height))
{
Point ptParentScreen = Parent.Location;
Point ptScreen = Parent.PointToScreen(Location);
Point ptInParent;

if (Parent.Parent != null)
{
ptParentScreen =
Parent.Parent.PointToScreen(ptParentScreen);
}

ptInParent = ptScreen - new Size(ptParentScreen);

_fDontDraw = true;
Parent.DrawToBitmap(bmpT, new Rectangle(new
Point(), Parent.Size));
_fDontDraw = false;

_bmpBackground = new Bitmap(Width, Height);
using (Graphics gfxT =
Graphics.FromImage(_bmpBackground))
{
gfxT.DrawImageUnscaled(bmpT, new Point() -
new Size(ptInParent));
}
}
}

if (_bmpBackground != null)
{
pe.Graphics.DrawImage(_bmpBackground, new Point());
}

// Calling the base class OnPaint
base.OnPaint(pe);
}
}

protected override void OnLocationChanged(EventArgs e)
{
if (_bmpBackground != null)
{
_bmpBackground.Dispose();
_bmpBackground = null;

Invalidate();
}

base.OnLocationChanged(e);
}
}
}
 
P

Peter Duniho

Okay, had a hunch that panned out. Instead of overriding
OnLocationChanged like this:

Peter said:
protected override void OnLocationChanged(EventArgs e)
{
if (_bmpBackground != null)
{
_bmpBackground.Dispose();
_bmpBackground = null;

Invalidate();
}

base.OnLocationChanged(e);
}

Override SetBoundsCore like this:

protected override void SetBoundsCore(int x, int y, int width,
int height, BoundsSpecified specified)
{
if (_bmpBackground != null)
{
_bmpBackground.Dispose();
_bmpBackground = null;

Invalidate();
}

base.SetBoundsCore(x, y, width, height, specified);
}

That method gets called before the on-screen update occurs, allowing you
to reset the background before that happens. By doing this, the
smudging effect goes away.

Other that the potential performance issues, this actually might work
okay for you. :) For what it's worth though, I did try it with the
form sized much larger, and it definitely slows down noticeably. :(
 
B

Brian Ward

Thanks Again Peter
=================
I'll have a go with this although I hadn't realised I had stepped into
quite such a big pile of smelly stuff!
I thought it would be such a straightforward thing to do. Heh-ho!
Thanks for your patience and diligence.
===
Brian
===

The message <[email protected]>
from Peter Duniho said:
Okay, had a hunch that panned out. Instead of overriding
OnLocationChanged like this:
Override SetBoundsCore like this:
protected override void SetBoundsCore(int x, int y, int width,
int height, BoundsSpecified specified)
{
if (_bmpBackground != null)
{
_bmpBackground.Dispose();
_bmpBackground = null;

base.SetBoundsCore(x, y, width, height, specified);
}
 
P

Peter Duniho

Brian said:
Thanks Again Peter
=================
I'll have a go with this although I hadn't realised I had stepped into
quite such a big pile of smelly stuff!
I thought it would be such a straightforward thing to do. Heh-ho!

Me too. Not having ever had a transparent control overlapping any other
control, I never realized that "Transparent" doesn't actually mean
"transparent", but instead only means "draw the background with the
parent's background color".

IMHO, that's a pretty significant difference in meaning, and also IMHO
the docs are wrong here. Not only does the doc page for
Control.BackColor not describe this discrepancy, it even implies that
transparent colors can be supported when it discusses the control style
SupportsTransparentBackColor as being required for using transparent
color. All that a "transparent" color does is blend with the parent's
background color; it's still not really transparent.
Thanks for your patience and diligence.

You're welcome. One other point I might as well make, though you likely
already understand it: the performance hit in the technique I posted is
because the entire parent has to be copied with DrawToBitmap every time
that the control moves. If you can avoid this, performance should be
fine; the only way I know to avoid it is if the parent's contents are
static, but of course if you figured out some way to get DrawToBitmap to
draw only the portion of the parent you are interested in that would
work as well.

You might also have some success with an "in-between" solution in which
you only update the background bitmap when the parent contents change.
If the parent isn't static, but also doesn't change very often, that
could be a better approach.

Pete
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top