原文地址:http://www.cnblogs.com/jxsoft/archive/2011/03/09/1978153.html
(1)
制作透明窗体办法有好几种,各有优缺点.
我们先来看看C#本身提供的办法
1:通过设置窗体的 TransparencyKey实现
例:窗体中的白色会变成透明
this
.BackColor
=
Color.White;
this
.TransparencyKey
=
Color.White;
2:通过设置窗体的 Opacity 实现
例:窗体的透明度为50%
this
.Opacity
=
0.5
;
为了更好说明问题,我们先看一个实例。
(1)新建一个winform应用程序
(2)FormLoad中添加以下代码:
this.BackColor = Color.White;
this.TransparencyKey = Color.White;
(3)加入一个Label,Text = "TransparencyKey Demo",Backcolor = White(或透明)
(4)按F5运行如下图所示

粗心的朋友对上图要看仔细了,是不是发现“TransparencyKey Demo”文字带白边?如果你把该窗口拖到一个正在播放的视频上就会看得更清楚。
结论:TransparencyKey只支持透明或不透明,不支持过度色,比如PNG图片中的从不透明到透明的过渡色会显示出讨厌的效果。
再来看看Opacity,我们发现它的显示效果很好,不存在TransparencyKey的过度色问题,但是Opacity设置的是窗体的透明度,和普通窗口相比,只是透明度不同而已,其他没有区别。如果你把通过Opacity制作的透明窗体盖在其他程序的窗口上,你看得到后面的窗口,却没法直接操作后面的窗体。并且该窗口上所有的控件也会产生透明效果,无法单独控制。
最后,我要说的是,不管TransparencyKey的实现,还是Opacity的实现,其实都是对SetLayeredWindowAttributes()函数的封装,只是参数不同而已。
在下一篇文章里,我们将实现更多的效果,但不再讨论SetLayeredWindowAttributes(),因为它的2种功能已被微软封装成TransparencyKey和Opacity,大家只要用就行了。
(2)
上文讨论了C#本身提供的2种透明窗体制作方法,但在有些情况下满足不了需要,这里再提供2种方法供参考。UpdateLayeredWindow()和GraphicsPath。
关于用Windows api的SetLayeredWindowAttributes(),bitblt()制作透明窗体在这里不再讨论。SetLayeredWindowAttributes()可参照上文,bitblt()类似于UpdateLayeredWindow()。
(1)UpdateLayeredWindow就是把一幅图片(有alpha通道)作为窗体,做出的显示效果非常好,同时问题也来了,窗体上的任何控件是看不到的。比如,要想窗体上的一个按钮响应鼠标事件,你只能在窗体中捕获该事件,通过该事件的发生位置来判断是否由按钮来响应。对于包含多个控件的窗体来说,这将是一场灾难(至少我没实现通用性)。如果你只是想画一条鱼,在桌面上游来游去,倒是不错的选择。
基本实现:
1. 新建一WindowsForm应用程序
2.重载窗体的OnHandleCreated方法
代码
protected
override
void
OnHandleCreated(EventArgs e)
{
SetStyle(ControlStyles.AllPaintingInWmPaint,
true
);
SetStyle(ControlStyles.UserPaint,
true
);
UpdateStyles();
base
.OnHandleCreated(e);
}
3.重载窗体的CreateParams方法
代码
protected
override
CreateParams CreateParams
{
get
{
CreateParams cParms
=
base
.CreateParams;
cParms.ExStyle
|=
0x00080000
;
//
WS_EX_LAYERED
return
cParms;
}
}
4.调用UpdateLayeredWindow()方法。this.BackgroundImage为你事先准备的带透明图片。
代码
private void Form1_Load(object sender, EventArgs e)
{
Bitmap bitmap =new Bitmap(this.BackgroundImage);
SetBits(bitmap);
}
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);
}
}
(2)GraphicsPath是通过设置窗口的不规则区域来实现,在动态方面效率不如UpdateLayeredWindow,但优点也很明显,你可以在窗体里加控件,并响应你的操作。你的控件就像你的窗口一样可以实现不规则形状。(用ImageBox也很好实现不规则按钮)
实现:
1. 新建一WindowsForm应用程序
2. 实现获取不规则区域函数(为了突出重点,算法没有优化)。
代码
private
GraphicsPath GetWindowRegion(Bitmap bitmap)
{
Color TempColor;
GraphicsPath gp
=
new
GraphicsPath();
if
(bitmap
==
null
)
return
null
;
for
(
int
nX
=
0
; nX
<
bitmap.Width; nX
++
)
{
for
(
int
nY
=
0
; nY
<
bitmap.Height; nY
++
)
{
TempColor
=
bitmap.GetPixel(nX, nY);
if
(TempColor
!=
Color.Transparent)
{
gp.AddRectangle(
new
Rectangle(nX, nY,
1
,
1
));
}
}
}
return
gp;
}
3. 设置窗体区域。控件如果有Region属性,也类似,读者可自己实现。

176

被折叠的 条评论
为什么被折叠?



