.net 窗话柄现比较完美玻璃透明效果
为了实现.net 下窗口玻璃透明效果,在网上找了很多例子,废了一番功夫,但是还是没有找到完美的例子。CodeProject上有一些例子,有一篇好像是国人写的,不过还是不够完美。
在win7以上系统实现玻璃效果,主要通过DwmExtendFrameIntoClientArea函数实现。当然,这个函数是操作系统API。不过这个在.net 窗口里使用的话,会出现一些问题,就是窗口里控件(比如按钮)的字体会变成透明色,会和玻璃和桌面背景混合。反正就是具备了一定的透明度,这样效果不是很好。有一种不是很完美的解决办法,就是利用Dwm函数绘制文本,或者使用C++里面的TextOut,但是感觉都不完美。
后来一直在找方法,研究了.net下窗口的工作原理,得出结论是无法完美实现透明效果。
无聊之中,不断测试了各种情况下,TransparencyKey各种颜色效果,包括不设置TransparencyKey。结果突然发现,在一种颜色下,控件上的文本不再变透明了!这种颜色是一种淡蓝色。因为之前设置的窗口背景色和TransparencyKey都是同一种颜色,而且要么是黑色,要么是白色,要么是灰色,而淡蓝色可以完美实现玻璃效果,我猜测,只要不是灰度色,那么都可以实现完美玻璃效果。经测试,证明了这一点。
为了完美实现玻璃透明效果和玻璃处点击可以拖动窗口的效果,有以下要点:
1、在适当的地方调用DwmExtendFrameIntoClientArea,比如在Load函数处。如果要实现全玻璃,可以让页距4个边都为-1;
2、TranseparencyKey要设置一种颜色,窗口背景色也设置成同一种颜色,并且颜色都为非灰度颜色,换句话说,不能是黑白电视机屏幕的颜色。
3、处理WndProc函数,让客户处的点击都视为拖动,非客户区让原有WndProc函数继续处理(不然没法拖动窗口大小)。
4、不必在WndProc函数修改窗口客户端大小。
5、不必使用Dwm 函数绘制带发光字体的文本。
下面是代码,部分代码和效果:
Color k = Color.Brown; this.BackColor = k; this.TransparencyKey = k; SetStyle(ControlStyles.ResizeRedraw, true); DoubleBuffered = true; CheckGlassEnabled(); dwmMargins.cyBottomHeight = -1; dwmMargins.cyTopHeight = -1; dwmMargins.cxLeftWidth = -1; dwmMargins.cxRightWidth = -1; //dwmMargins.cyBottomHeight = this.ClientSize.Height - listView1.Bottom; //dwmMargins.cyTopHeight = listView1.Top; //dwmMargins.cxLeftWidth = listView1.Left; //dwmMargins.cxRightWidth =listView1.Left; Dwm.DwmExtendFrameIntoClientArea(this.Handle, ref dwmMargins);
protected override void WndProc(ref Message m) { int HTCAPTION = 2; int WM_NCCALCSIZE = 0x83; int WM_NCHITTEST = 0x84; IntPtr result; if (m.Msg == WM_NCHITTEST && (int)m.Result == 0) { m.Result = HitTestNCA(m.HWnd, m.WParam, m.LParam); //m.Result = new IntPtr(HTCAPTION); if (m.Result.ToInt32() != HTCAPTION) base.WndProc(ref m); } else base.WndProc(ref m); }
private IntPtr HitTestNCA(IntPtr hwnd, IntPtr wparam, IntPtr lparam) { int HTTRANSPARENT = -1; int HTNOWHERE = 0; int HTCLIENT = 1; int HTCAPTION = 2; int HTGROWBOX = 4; int HTSIZE = HTGROWBOX; int HTMINBUTTON = 8; int HTMAXBUTTON = 9; int HTLEFT = 10; int HTRIGHT = 11; int HTTOP = 12; int HTTOPLEFT = 13; int HTTOPRIGHT = 14; int HTBOTTOM = 15; int HTBOTTOMLEFT = 16; int HTBOTTOMRIGHT = 17; int HTREDUCE = HTMINBUTTON; int HTZOOM = HTMAXBUTTON; int HTSIZEFIRST = HTLEFT; int HTSIZELAST = HTBOTTOMRIGHT; Point p = new Point(LoWord((int)lparam), HiWord((int)lparam)); Rectangle client = RectangleToScreen(ClientRectangle); if (client.Contains(p)) return new IntPtr(HTCAPTION); return new IntPtr(HTNOWHERE); }
public static int LoWord(int dwValue) { return dwValue & 0xFFFF; } public static int HiWord(int dwValue) { return (dwValue >> 16) & 0xFFFF; }