您的位置:首页 > 编程语言 > C#

C#绘制透明区域

2010-12-18 20:19 330 查看
环境: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属性)取消。这样标题栏就没了。仅留下边框。我在客户区伪造一个标题栏。效果如何改天试验(主要是杜绝边框的闪烁)。晚饭还没吃,先吃饭去了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: