C# 实现真正的透明控件(Windows桌面程序)
来源:互联网 发布:数据分析师修炼 编辑:程序博客网 时间:2024/04/29 21:06
由于上位机程序的需要,需要大量的异性控件,开始不以为然,心想随着GDI到GDI+的升级,微软应该会给NET打造了一套高效绚丽的绘图方案,使用后才知道完全不是我以为的那么回事.于是开始各种寻找资源,发现网上方法无非使用Web.Transparent作为背景,要么就完全使用控件背后作为控件本身背景,然而这些无非都是掩耳盗铃,真正意义上的透明完全没有做到。
1.其中使用Web.Transparent的透明方案:
Label使用的的透明方案
如图,这种透明系统参考的是控件所属控件背景色,并不是透明,注意左边的两个红色框,其实是两个实现真正透明的控件
2.而通过背景截图来作为自己背景图的方案:
由于需要编写太多代码,就不举例了,看起来挺不错,但也经不起推敲,毕竟很多时候,透明者需要盖住一些与用户交互的控件,可想而知,这种设计将是致命的。
3.因为Form有TransparencyKey属性,很容易实现真正的透明,就想干脆继承Form来做控件,使用的时候,直接SetParent岂不美好,先不说资源耗费,用户体验.单单位置控制都够呛,其消息队列性质的不同,带来的问题,完全不可取,而且在窗体在被指定到窗体后,其透明属性竟然失效了,大写的囧
难道就没办法了吗??由于透明控件的实现尚未有好的方法,该想法被搁置了半年.
某个偶尔的机会,接触到了Region对象,关于该对象详细信息请参阅微软的官方文档,但是官方文档也只是机械的介绍了该对象成员而已,看不出什么蹊跷的。
我是这么理解这个对象的,该对象告诉了系统这个控件需要占用的界面UI信息,而且这个信息是可以随意编辑的,,,,好知道这么多就够了,如果我把这个对象编辑成我要的形状呢?是不是其他不需要的就消失了?怀着这样疑问写下代码:
Region rion = new Region(new Rectangle(0, 0, 20, 20));Region = rion;乖乖,控件竟然无论我怎么绘制,在窗口上都只有 20×20 大了,尽管我拖得了很大
内心一阵狂喜,,似乎找到了希望
而情况也恰是如此,通过控制Region的信息,完全可以控制控件需要现实和不现实(透明)的部分,
那么问题来了,一些规则的透明还好办,但是如果需要按某种特定无规则来异形呢..难道要一点点去算吗?那也太不科学了,想到这里,自然想到通过 Image(Bitemap)转换成Region
首先看Region的构造函数
使用一个GraphicsPath 对象来构造,通过搜索图像的每个像素,来将需要显示的区域添加的路径画布里,是否可行呢!
由于主题关系,这里延伸对GraphicsPath对象讲解,不熟悉的朋友参考相关资料,谢谢。
根据这一想法,编写转换代码
/// <summary> /// 根据图片计算GraphicsPath路径(低效率) /// </summary> /// <param name="img">图像资源</param> /// <param name="TranColor">欲透明掉的颜色</param> /// <returns>路径画布,已过滤掉了透明颜色</returns> public static GraphicsPath ImageToGraphicsPath(Image imgx,Color TranColor) { if (imgx == null) return null; GraphicsPath g = new GraphicsPath(FillMode.Alternate); Bitmap bitmap = null; if (typeof(Bitmap) == imgx.GetType()) bitmap = (Bitmap)imgx; else bitmap = new Bitmap(imgx); int ImWidth = bitmap.Width; int ImHeight = bitmap.Height; Color curColor; Rectangle curRect = new Rectangle(); curRect.Height = 1; bool isTransRgn; for (int y = 0; y < ImHeight; y++) { isTransRgn = true; for (int x = 0; x < ImWidth; x++) { curColor = bitmap.GetPixel(x, y); if (curColor == TranColor || x == ImWidth - 1)//如果遇到透明色或行尾 { if (isTransRgn == false)//退出有效区 { curRect.Width = x - curRect.X; g.AddRectangle(curRect); } } else//非透明色 { if (isTransRgn == true)//进入有效区 { curRect.X = x; curRect.Y = y; } }//if curColor isTransRgn = curColor == TranColor; } } return g; }结果如图:
达到目的...似乎任务完成了,,但是回头想想不对呀!为什么要通过GraphicsPath来中间转换呢?为什么不直接把坐标填充到Region 里呢,
因为Region 有一个方法是:
通过GraphicsPath来转换,可能带来其他资源类问题,直接使用这方法估计是不错的选择,于是增加函数:
/// <summary> /// 根据图片计算Region路径(低效率) /// </summary> /// <param name="img">图像资源</param> /// <param name="TranColor">欲透明掉的颜色</param> /// <returns>一个离散的路径信息</returns> public static Region ImageToRegion(Image imgx, Color TranColor) { if (imgx == null) return null; Region rRegion = new Region(); rRegion.MakeEmpty(); Bitmap bitmap = null; if (typeof(Bitmap) == imgx.GetType()) bitmap = (Bitmap)imgx; else bitmap = new Bitmap(imgx); int ImWidth = bitmap.Width; int ImHeight = bitmap.Height; Color curColor; Rectangle curRect = new Rectangle(); curRect.Height = 1; bool isTransRgn; for (int y = 0; y < ImHeight; y++) { isTransRgn = true; for (int x = 0; x < ImWidth; x++) { curColor = bitmap.GetPixel(x, y); if (curColor == TranColor || x == ImWidth - 1)//如果遇到透明色或行尾 { if (isTransRgn == false)//退出有效区 { curRect.Width = x - curRect.X; rRegion.Union(curRect); } } else//非透明色 { if (isTransRgn == true)//进入有效区 { curRect.X = x; curRect.Y = y; } }//if curColor isTransRgn = curColor == TranColor; } } return rRegion; }结果同样成功,就不在上图..
然而似乎没什么问题了,但是细心的朋友可能感觉到了,这种方法读取图像资源是一种极度效率低下的方法,这种直接
GetPixel怎么都不像用在如此大量图像处理上的,,,如果用来展示动画类,岂不够呛!
这里不对C#指针,图像处理的知识扩展,不安全代码等知识扩展。尽管我们在接下来的函数使用到相关知识,如果有兴趣的请自行参阅相关文档
修改相关函数提升效率,减少资源耗费
/* * 为了鼓励学习研究精神,该函数,仅能用于本示例 * 如果使用到其他项目,可能会存在错误 * 如果您确实需要正确代码,请学习位图相关信息 */ /// <summary> /// 取得一个图片中非透明色部分的区域。 /// </summary> /// <param name="Picture">取其区域的图片。</param> /// <param name="TransparentColor">透明色。</param> /// <returns>图片中非透明色部分的区域</returns> public unsafe static Region ImageToRegionPx(Image Picture, Color TransparentColor) { if (Picture == null) return null; Region rgn = new Region(); rgn.MakeEmpty(); Bitmap bitmap = null; if (Picture.GetType() != typeof(Bitmap)) bitmap = new Bitmap(Picture); else bitmap = (Bitmap)Picture; int width = bitmap.Width; int height = bitmap.Height; BitmapData bmData = bitmap.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); byte* p = (byte*)bmData.Scan0; int offset = bmData.Stride - width * 3; int p0, p1, p2; // 记录透明色 p0 = TransparentColor.R; p1 = TransparentColor.G; p2 = TransparentColor.B; Rectangle curRect = new Rectangle(); curRect.Height = 1; int start = -1; // 行座标 ( Y col ) for (int Y = 0; Y < height; Y++) { // 列座标 ( X row ) for (int X = 0; X < width; X++) { if (start == -1 && (p[0] != p0 || p[1] != p1 || p[2] != p2)) //如果 之前的点没有不透明 且 不透明 { start = X; //记录这个点 curRect.X = X; curRect.Y = Y; } else if (start > -1 && (p[0] == p0 && p[1] == p1 && p[2] == p2)) //如果 之前的点是不透明 且 透明 { curRect.Width = X - curRect.X; rgn.Union(curRect); start = -1; } if (X == width - 1 && start > -1) //如果 之前的点是不透明 且 是最后一个点 { curRect.Width = X - curRect.X; rgn.Union(curRect); start = -1; } p += 3;//下一个内存地址 } p += offset; } bitmap.UnlockBits(bmData); bitmap.Dispose(); return rgn; }
到此,真正的C#桌面程序透明控件设计完成...
关于其他双缓存,半透明,请参考其他相关知识
相关源代码(VS2008)开发
http://download.csdn.net/detail/yangshengchuan/9749122
- C# 实现真正的透明控件(Windows桌面程序)
- C#中实现真正的透明的PictrueBox
- C# windows 桌面控件的扩展
- C# WINFORM 制作真正的 透明窗体 桌面画图 的完美解决方案
- Windows Mobile实现透明控件
- Windows Mobile实现透明控件
- C#中实现透明控件
- C#实现透明WinForm控件
- C#实现透明背景的垂直Label控件
- Transparent Desktop Video C#实现透明桌面视频播放源代码
- C# WINFORM 使用鼠标钩子实现透明窗体桌面画图
- 【C#】透明控件的实现
- 实现控件的透明背景
- 桌面透明窗口程序渲染
- 透明的windows media player控件
- C#怎样实现窗体透明而控件不透明,或者就是怎样无窗体显示图片,就像圣诞那样的程序,不吝赐教!
- 第一个真正的Windows程序
- 第一个真正的Windows程序
- 友盟消息推送u-push
- butterknife源码分析:谈一谈Java的注解
- java 向上转型向下转型
- 免费的论文查重网站
- Esp8266 从WiFi连接讲解其中的必备函数
- C# 实现真正的透明控件(Windows桌面程序)
- AsyncTask异步任务 android
- iOS之ProtocolBuffer搭建和示例demo
- 如何在同一台电脑上使用两个Git账户
- CJOJ P2298 【NOI2016】区间
- android的Binder框架
- Using scatter plots for multivariate data —— python data science cookbook
- 区间合并
- U3D[02.07]