上文讨论了C#本身提供的2种透明窗体制作方法,但在有些情况下满足不了需要,这里再提供2种方法供参考。UpdateLayeredWindow()和GraphicsPath。
关于用Windows api的SetLayeredWindowAttributes(),bitblt()制作透明窗体在这里不再讨论。SetLayeredWindowAttributes()可参照上文,bitblt()类似于UpdateLayeredWindow()。
(1)UpdateLayeredWindow就是把一幅图片(有alpha通道)作为窗体,做出的显示效果非常好,同时问题也来了,窗体上的任何控件是看不到的。比如,要想窗体上的一个按钮响应鼠标事件,你只能在窗体中捕获该事件,通过该事件的发生位置来判断是否由按钮来响应。对于包含多个控件的窗体来说,这将是一场灾难(至少我没实现通用性)。如果你只是想画一条鱼,在桌面上游来游去,倒是不错的选择。
基本实现:
1. 新建一WindowsForm应用程序
2.重载窗体的OnHandleCreated方法
![ContractedBlock.gif](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![ExpandedBlockStart.gif](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
{
SetStyle(ControlStyles.AllPaintingInWmPaint, true );
SetStyle(ControlStyles.UserPaint, true );
UpdateStyles();
base .OnHandleCreated(e);
}
3.重载窗体的CreateParams方法
![ContractedBlock.gif](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![ExpandedBlockStart.gif](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
{
get
{
CreateParams cParms = base .CreateParams;
cParms.ExStyle |= 0x00080000 ; // WS_EX_LAYERED
return cParms;
}
}
4.调用UpdateLayeredWindow()方法。this.BackgroundImage为你事先准备的带透明图片。
![ContractedBlock.gif](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![ExpandedBlockStart.gif](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
private void Form1_Load(object sender, EventArgs e)
{
Bitmap bitmap =new Bitmap(this.BackgroundImage);
SetBits(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. 实现获取不规则区域函数(为了突出重点,算法没有优化)。
![ContractedBlock.gif](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![ExpandedBlockStart.gif](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
{
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属性,也类似,读者可自己实现。
![ContractedBlock.gif](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![ExpandedBlockStart.gif](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
{
Bitmap bitmap = new Bitmap( this .BackgroundImage);
this .Region = new Region(GetWindowRegion(bitmap));
}
全文完。