C#绘制透明区域

环境:VS2005 WinForm

 

第一类方法:设置WS_EX_LAYERED窗口风格

 

C#设置透明色的最简单办法是设置窗口TransparencyKey属性。该属性相当于Win32:

            (示例代码,一些关键参数如窗口句柄、颜色值决定于程序需求)

            SetWindowLong(Handle, GWL_EXSTYLE, GetWindowLong(Handle, GWL_EXSTYLE) | WS_EX_LAYERED);
            SetLayeredWindowAttributes(Handle, RGB(0, 0, 0), 0, 0x1);

 

该方法存在两个弊端:

1:窗口加载时闪烁严重,但已有不错的解决办法,参见:

 

http://topic.csdn.net/u/20100921/14/724f3ca4-c083-42b9-b8c3-35bec4db9cbd.html

 

2:将设置了透明色的WinForm窗体放在PPLIVE上(PPLIVE正在播放任意节目)。该WinForm窗体会狂闪不止。

原因只知道和PPLIVE的刷新有关。解决办法至今未找到。

 

 

在设置了WS_EX_LAYERED风格后,还有一个办法可以设置透明,就是利用UpdateLayeredWindow:

示例代码:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;

namespace WindowsApplication18
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        [DllImport("user32.dll", EntryPoint = "GetWindowLong")]
        public static extern long GetWindowLong(IntPtr hwnd, int nIndex);
        [DllImport("user32.dll", EntryPoint = "SetWindowLong")]
        public static extern long SetWindowLong(IntPtr hwnd, int nIndex, long dwNewLong);
        [DllImport("user32", EntryPoint = "SetLayeredWindowAttributes")]
        public static extern int SetLayeredWindowAttributes(IntPtr Handle, int crKey, byte bAlpha, int dwFlags);
        const int GWL_EXSTYLE = -20;
        const int WS_EX_TRANSPARENT = 0x20;
        const int WS_EX_LAYERED = 0x80000;
        const int LWA_ALPHA = 2;

        private void Form1_Load(object sender, EventArgs e)
        {
            SetWindowLong(Handle, GWL_EXSTYLE, GetWindowLong(Handle, GWL_EXSTYLE) | WS_EX_LAYERED);

            Bitmap bitmap = new Bitmap(this.BackgroundImage);
            SetBits(bitmap);
        }

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

        public void SetBits(Bitmap bitmap)
        {
            //if (!haveHandle) return;
            if (!Bitmap.IsCanonicalPixelFormat(bitmap.PixelFormat) || !Bitmap.IsAlphaPixelFormat(bitmap.PixelFormat))
                throw new ApplicationException("图片必须是32位带Alhpa通道的图片。");
            IntPtr oldBits = IntPtr.Zero;
            IntPtr screenDC = Win32.GetDC(IntPtr.Zero);
            IntPtr hBitmap = IntPtr.Zero;
            IntPtr memDc = Win32.CreateCompatibleDC(screenDC);
            try {
                Win32.Point topLoc = new Win32.Point(Left, Top);
                Win32.Size bitMapSize = new Win32.Size(bitmap.Width, bitmap.Height);
                Win32.BLENDFUNCTION blendFunc = new Win32.BLENDFUNCTION();
                Win32.Point srcLoc = new Win32.Point(0, 0);
                hBitmap = bitmap.GetHbitmap(Color.FromArgb(0));
                oldBits = Win32.SelectObject(memDc, hBitmap);
                blendFunc.BlendOp = Win32.AC_SRC_OVER;
                blendFunc.SourceConstantAlpha = 255;
                blendFunc.AlphaFormat = Win32.AC_SRC_ALPHA;
                blendFunc.BlendFlags = 0;
                Win32.UpdateLayeredWindow(Handle, screenDC, ref topLoc, ref bitMapSize, memDc, ref srcLoc, 0, ref blendFunc, Win32.ULW_ALPHA);
            }
            finally
            {
                if (hBitmap != IntPtr.Zero)
                {
                    Win32.SelectObject(memDc, oldBits);
                    Win32.DeleteObject(hBitmap);
                }
                Win32.ReleaseDC(IntPtr.Zero, screenDC);
                Win32.DeleteDC(memDc);
            }
        }
    }

    // a static class to expose needed win32 gdi functions.
    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 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);
    }
}

 

这种办法克服了第一种方法会造成闪烁的缺点。不过该方法有个大大的毛病:放在透明区域的控件看不见了。解决办法网上有,比较麻烦。

不仅如此,上面示例程序中,透明区域覆盖了整个客户区。(图片中间是一个微软的圆形视窗图标,周围透明色)运行我发现窗口边框也不显示了!仅能看见那个圆圆的图标。这对于做异形窗口正好。如果程序需要边框,就悲剧了。

 

 

第二类方法:设置System.Drawing.Region

 

该方法不存在闪烁问题,同样适合做异形窗口。透明区域可以自己来设置,也可根据图片来设置。示例:

http://blog.csdn.net/CIIIC/archive/2007/10/05/1812137.aspx

 

缺点是同样涉及到边框。当你的剪切区域和窗体一样大时,窗体边框的XP样式就不见了。变得非常难看。而且你想通过处理WM_NCPAINT来自绘边框也不理想,因为无法做到真正的完全自绘。当窗口Z序变化时,旧的绘制老是跳出来,造成闪烁。(已有网友各种努力,还是无法完全解决)

 

我目前想到一个办法:将窗体标题栏控制按钮取消,标题(Text属性)取消。这样标题栏就没了。仅留下边框。我在客户区伪造一个标题栏。效果如何改天试验(主要是杜绝边框的闪烁)。晚饭还没吃,先吃饭去了。

 

 

 

 

 

你可能感兴趣的:(struct,C#,user,Class,byte,WinForm)