Click here to Skip to main content
15,895,557 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more: , +
Is it possible to create an irregular window with per pixel alpha so that it
blends in to the desktop at the edges? I've managed to create an irregular
from using the colour-keyed TransparentColour property, but this leaves it
with a hard aliased edge.

I found something, but this is what I get:
<img src="http://img440.imageshack.us/img440/5829/what234234.jpg" height="500px" />

Of course I have buttons, and other things behind what you see there. But It doesn't want to show correctly. Any fix for this, would be amazing. I think it's because I'm only alpha'ing the background image, which loads it ontop of everything. But again, I have no clue on how to alpha JUST the background, and keep it as a background.

My code for Per-Pixel-Alpha:

C#
class Win32
{
    public enum Bool
    {
        False = 0,
        True
    };


    [StructLayout(LayoutKind.Sequential)]
    public struct Point
    {
        public Int32 x;
        public Int32 y;

        public Point(Int32 x, Int32 y) { this.x = x; this.y = y; }
    }


    [StructLayout(LayoutKind.Sequential)]
    public struct Size
    {
        public Int32 cx;
        public Int32 cy;

        public Size(Int32 cx, Int32 cy) { this.cx = cx; this.cy = cy; }
    }


    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    struct ARGB
    {
        public byte Blue;
        public byte Green;
        public byte Red;
        public byte Alpha;
    }


    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    public struct BLENDFUNCTION
    {
        public byte BlendOp;
        public byte BlendFlags;
        public byte SourceConstantAlpha;
        public byte AlphaFormat;
    }

    public const Int32 WS_EX_LAYERED = 0x80000;
    public const Int32 ULW_COLORKEY = 0x00000001;
    public const Int32 ULW_ALPHA = 0x00000002;
    public const Int32 ULW_OPAQUE = 0x00000004;

    public const byte AC_SRC_OVER = 0x00;
    public const byte AC_SRC_ALPHA = 0x01;


    [DllImport("user32.dll", ExactSpelling = true, SetLastError = true)]
    public static extern Bool UpdateLayeredWindow(IntPtr hwnd, IntPtr hdcDst, ref Point pptDst, ref Size psize, IntPtr hdcSrc, ref Point pprSrc, Int32 crKey, ref BLENDFUNCTION pblend, Int32 dwFlags);

    [DllImport("user32.dll", ExactSpelling = true, SetLastError = true)]
    public static extern IntPtr GetDC(IntPtr hWnd);

    [DllImport("user32.dll", ExactSpelling = true)]
    public static extern int ReleaseDC(IntPtr hWnd, IntPtr hDC);

    [DllImport("gdi32.dll", ExactSpelling = true, SetLastError = true)]
    public static extern IntPtr CreateCompatibleDC(IntPtr hDC);

    [DllImport("gdi32.dll", ExactSpelling = true, SetLastError = true)]
    public static extern Bool DeleteDC(IntPtr hdc);

    [DllImport("gdi32.dll", ExactSpelling = true)]
    public static extern IntPtr SelectObject(IntPtr hDC, IntPtr hObject);

    [DllImport("gdi32.dll", ExactSpelling = true, SetLastError = true)]
    public static extern Bool DeleteObject(IntPtr hObject);
}

protected void OnPaintBackground(Bitmap bitmap, byte opacity)
{
    if (bitmap.PixelFormat != PixelFormat.Format32bppArgb)
        throw new ApplicationException("The bitmap must be 32ppp with alpha-channel.");

    // The ideia of this is very simple,
    // 1. Create a compatible DC with screen;
    // 2. Select the bitmap with 32bpp with alpha-channel in the compatible DC;
    // 3. Call the UpdateLayeredWindow.

    IntPtr screenDc = Win32.GetDC(IntPtr.Zero);
    IntPtr memDc = Win32.CreateCompatibleDC(screenDc);
    IntPtr hBitmap = IntPtr.Zero;
    IntPtr oldBitmap = IntPtr.Zero;

    try
    {
        hBitmap = bitmap.GetHbitmap(Color.FromArgb(0));  // grab a GDI handle from this GDI+ bitmap
        oldBitmap = Win32.SelectObject(memDc, hBitmap);

        Win32.Size size = new Win32.Size(bitmap.Width, bitmap.Height);
        Win32.Point pointSource = new Win32.Point(0, 0);
        Win32.Point topPos = new Win32.Point(Left, Top);
        Win32.BLENDFUNCTION blend = new Win32.BLENDFUNCTION();
        blend.BlendOp = Win32.AC_SRC_OVER;
        blend.BlendFlags = 0;
        blend.SourceConstantAlpha = opacity;
        blend.AlphaFormat = Win32.AC_SRC_ALPHA;

        Win32.UpdateLayeredWindow(Handle, screenDc, ref topPos, ref size, memDc, ref pointSource, 0, ref blend, Win32.ULW_ALPHA);
    }
    finally
    {
        Win32.ReleaseDC(IntPtr.Zero, screenDc);
        if (hBitmap != IntPtr.Zero)
        {
            Win32.SelectObject(memDc, oldBitmap);
            //Windows.DeleteObject(hBitmap); // The documentation says that we have to use the Windows.DeleteObject... but since there is no such method I use the normal DeleteObject from Win32 GDI and it's working fine without any resource leak.
            Win32.DeleteObject(hBitmap);
        }
        Win32.DeleteDC(memDc);
    }
}

protected override CreateParams CreateParams
{
    get
    {
        // Add the layered extended style (WS_EX_LAYERED) to this window
        CreateParams createParams = base.CreateParams;
        createParams.ExStyle |= Win32.WS_EX_LAYERED;
        return createParams;
    }
}

private void main_Load(object sender, EventArgs e)
{
    OnPaintBackground(Properties.Resources.Background, 255);
}


Any idea how I could only per-pixel alpha the background.png image ONLY, then place it into the form background property?
Posted

1 solution

These experiments may give interesting results but they have very little practical value because you are not even trying to make a really irregular shape. The difference is easy to see: no matter what you do, if you click a mouse withing the rectangular window bounds, you will activate this window while with really non-rectangular shape you could activate the window below it. For example, if you had a hole inside you window, a click inside the hole would activate a window below.

But this is amazingly easy to achieve! You only should assign new value to the System.Windows.Forms.Control.Region, where Control can be your form. If the region is of irregular shape, you form will assume truly irregular shape.

See http://msdn.microsoft.com/en-us/library/system.windows.forms.control.region.aspx[^].

(And, to best of my knowledge, it is not possible in principle — to achieve modulated semi-transparency using your method.)

—SA
 
Share this answer
 
v2
Comments
shadowevil 7-Jan-12 2:36am    
Is there any way I can contact you, so you can help me achieve this? I am totally lost on how to use this "Region control." Thanks in advance!!!

You can email me, or add me on MSN:
shadow3vi1@hotmail.com
Yahoo:
shadow.3vi1@yahoo.com
AIM:
shadow3vi1
Skype:
shadow3vi1
Sergey Alexandrovich Kryukov 1-Apr-12 20:37pm    
Please don't leave any direct contact unless you like spam. You can contact me via my Web site you can find in my CodeProject profile; it has a "contact me" page. I cannot promise much, but we can talk about your problem.

Alternatively, you can use "Improve question" and explain the problem. I think I answered it in full. It is not "Region control", it's Control.Region. The MSDN help page explains it all.
--SA
Member 8110973 1-Apr-12 18:19pm    
I'm also having this problem. The irregular background appears correctly, but form controls are not visible, although the events are fired.

I´ve been working in how to solve this, but nothing that I do works. Please help me!
Sergey Alexandrovich Kryukov 1-Apr-12 20:38pm    
You may want to ask your own question, but you should better create a complete but short code sample (with just one control, one form, one or two short files) manifesting the problem. The solution is really, really simple. I fail to imagine what goes wrong in you code, so...
--SA

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900