Question

I've been trying to display an image which has a transparent border as the background to a control.

Unfortunately, the transparent area creates a hole in the parent form as follows:

In the above image, the form has a red background which I'd hoped to see behind my control in the transparent areas.

The code I used is as follows:

    protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
    {
        if (this.Image != null)
        {
            Graphics g = Graphics.FromImage(this.Image);

            ImageAttributes attr = new ImageAttributes();

            //set the transparency based on the top left pixel
            attr.SetColorKey((this.Image as Bitmap).GetPixel(0, 0), (this.Image as Bitmap).GetPixel(0, 0));

            //draw the image using the image attributes.
            Rectangle dstRect = new Rectangle(0, 0, this.Image.Width, this.Image.Height);

            e.Graphics.DrawImage(this.Image, dstRect, 0, 0, this.Image.Width, this.Image.Height,
                GraphicsUnit.Pixel, attr);
        }
        else
        {
            base.OnPaint(e);
        }
    }

    protected override void OnPaintBackground(System.Windows.Forms.PaintEventArgs e)
    {
        //base.OnPaintBackground(e);
    }

This class is inherited from a PictureBox because I needed a control which implements OnMouseMove and OnMouseUp Events.

I've been researching most of the day without success testing out different ideas but unfortunately most only work on the full framework and not .Net CF.

Any ideas would be much appreciated.

Was it helpful?

Solution

Ah the joys of CF transparency. I could go on and on about it (and have in my blog and the Project Resistance code I did ages ago).

The gist is this. The child control has to paint it's areas, but first it has to call back up to it's parent (the Form in your case) and tell it to redraw it's background image everywhere except in the child's clipping region and then draw itself on top of that. If that sounds a bit confusing it's because it is.

For example, if you look at Project Resistance, a View (which is just a Control) draws a resistor and bands. It lies in a Form that has an image background, and that background needs to "show through" the transparent areas of the resistor:

enter image description here

So in the drawing code of the resistor it does this:

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

    try
    {
        RECT rect = new RECT(this.Bounds);

        // draw the blank
        Infrastructure.GraphicTools.DrawTransparentBitmap(e.Graphics, m_blankImage, Bounds, 
              new Rectangle(0, 0, m_blankImage.Width, m_blankImage.Height));

        if (m_bandsImage != null)
        {
            // draw the bands
            Infrastructure.GraphicTools.DrawTransparentBitmap(e.Graphics, m_bandsImage, Bounds, 
                 new Rectangle(0, 0, m_bandsImage.Width, m_bandsImage.Height));
        }
    }
    finally
    {
    }

    if (!Controller.TouchMode)
    {
        // TODO: draw in the selection arrow
        // Controller.SelectedBand
    }
}

Which is simple enough. The key is that it calls to it's base OnPaint, which does this:

protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
{
    // this assumes we're in a workspace, on MainForm (the whole Parent.Parent thing)
    IBackgroundPaintProvider bgPaintProvider = Parent.Parent as IBackgroundPaintProvider;
    if (bgPaintProvider != null)
    {
        Rectangle rcPaint = e.ClipRectangle;
        // use the parent, since it's the workspace position in the Form we want, 
        // not our position in the workspace
        rcPaint.Offset(Parent.Left, Parent.Top);
        bgPaintProvider.PaintBackground(e.Graphics, e.ClipRectangle, rcPaint);
    }
}

You can see it's calling PaintBackground of the containing Form (it's Parent.Parent in this case becuse the Control is actually in a container called a Workspace - you wouldn't need to walk up twice in your case). That draws in the background image in the area you're currently seeing as the "hole"

public void PaintBackground(Graphics g, Rectangle targetRect, Rectangle sourceRect)
{
    g.DrawImage(m_bmBuffer, targetRect, sourceRect, GraphicsUnit.Pixel);
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top