views:

1133

answers:

3

I have an image (PNG file) that has an alpha channel that is set to 50% opaque. When I try to draw the image on a form that has the TransparencyKey set to white, and the backcolor set to white I expect the image to be drawn 50% see-through. However, it is being blended with the forms backcolor first and as a result it is completely opaque. Is there any way around this? I don't want to set the form's Opaque property as some images on the form need to be translucent and some need to be opaque.

A: 

I don't think you can. We have a splash screen where we did something like this, but we ended up capturing the screen and setting it as the background image of the form. Obviously this only seems to work, if the screen changes, the background of the form does not and things look weird. If you find a better way of doing it, I would love to know about it.

Here is the code to capture the screen, just set the ScreenRect to the forms screen coordinates and call Process():

using System;
using System.Drawing;
using System.Runtime.InteropServices;

namespace TourFactory.Core.Drawing
{
    public class CaptureScreenCommand
    {

        #region Initialization and Destruction

        public CaptureScreenCommand()
        {
        }

        #endregion

        #region Fields and Properties

        // BitBlt is a multipurpose function that takes a ROP (Raster OPeration) code
        // that controls exactly what it does. 0xCC0020 is the ROP code SRCCOPY, i.e.
        // do a simple copy from the source to the destination.
        private const int cRasterOp_SrcCopy = 0xCC0020; // 13369376;

        private Rectangle mScreenRect;
        /// <summary>
        /// Gets or sets the screen coordinates to capture.
        /// </summary>
        public Rectangle ScreenRect
        {
            get { return mScreenRect; }
            set { mScreenRect = value; }
        }

        #endregion

        #region Methods

        public Image Process()
        {
            // use the GDI call and create a DC to the whole display
            var dc1 = CreateDC("DISPLAY", null, 0, 0);
            var g1 = Graphics.FromHdc(dc1);

            // create a compatible bitmap the size of the form
            var bmp = new Bitmap(mScreenRect.Width, mScreenRect.Height, g1);
            var g2 = Graphics.FromImage(bmp);

            // Now go retrace our steps and get the device contexts for both the bitmap and the screen
            // Note: Apparently you have to do this, and can't go directly from the aquired dc or exceptions are thrown
            // when you try to release the dcs
            dc1 = g1.GetHdc();
            var dc2 = g2.GetHdc();

            // Bit Blast the screen into the Bitmap
            BitBlt(dc2, 0, 0, mScreenRect.Width, mScreenRect.Height, dc1, mScreenRect.Left, mScreenRect.Top, cRasterOp_SrcCopy);

            // Remember to release the dc's, otherwise problems down the road
            g1.ReleaseHdc(dc1);
            g2.ReleaseHdc(dc2);

            // return bitmap
            return bmp;
        }

        #endregion

        #region gdi32.dll

        [DllImport("gdi32")]
        private static extern IntPtr CreateDC(string lpDriverName, string lpDeviceName, int lpOutput, int lpInitData);

        [DllImport("gdi32")]
        private static extern bool BitBlt(IntPtr hdcDest, int xDest, int yDest, int width, int height, IntPtr hdcSrc, int xSrc, int ySrc, int dwRop);

        #endregion

    }
}
Brian
+1  A: 

I ended up using a layered window, using the WS_EX_LAYERED extended window style.

http://msdn.microsoft.com/en-us/library/ms997507.aspx

Jon Tackabury
A: 

Nice. Don't forget that Vista has the Desktop Window Manager to create semi-transparent windows (aka Areo) http://msdn.microsoft.com/en-us/magazine/cc163435.aspx

bryanbcook
That's true, but I needed something that was backwards compatible with Windows XP.
Jon Tackabury