在 .NET 中如何实现真正的透明控件
.NET 里的控件背景色所谓的“透明背景”是通过绘制父控件的背景实现的,并不是真正的透明效果。
因此,当多个具有透明背景色的同级控件重叠时,Z 顺序位于前面的控件会遮挡住 Z 顺序小于它的控件。
现在我想要做一个能够真正实现透明背景的控件,思路如下:
1、模仿微软的做法,把和自己相交的控件的部分绘制为背景色
或者
2、使用传说中的 SetLayeredWindowAttribute 方法(未尝试过)。
对于方法1,在实施的时候出现了一些问题。
我新建了控件,继承于 Panel ,重写了控件的 OnPaitBackground 方法,代码如下:
protected override void OnPaintBackground(PaintEventArgs pevent) { paintingBg = true; base.OnPaintBackground(pevent); PaintTransparentBackground(pevent); paintingBg = false; } protected internal void PaintTransparentBackground(PaintEventArgs pevent) { Graphics g = pevent.Graphics; Rectangle myBounds = Bounds; foreach (Control ctrl in Parent.Controls) { if (ctrl != this && ctrl.Visible) { TransparentControl overlappedCtrl = ctrl as TransparentControl; if (null == overlappedCtrl || overlappedCtrl.paintingBg) { if (null == overlappedCtrl) { ctrl.Invalidate(); } continue; } g.ResetTransform(); PaintOverlappedControl(pevent, ctrl); } } g.ResetClip(); } private void PaintOverlappedControl(PaintEventArgs pe, Control overlappedCtrl) { Graphics g = pe.Graphics; Rectangle ctrlRect = overlappedCtrl.Bounds; if (ctrlRect.IntersectsWith(Bounds)) { Rectangle overlappedRect = Rectangle.Intersect(ctrlRect, Bounds); Point clipLeftTop = new Point(overlappedRect.Left - Left, overlappedRect.Top - Top); Rectangle clipRect = new Rectangle(clipLeftTop, overlappedRect.Size); Point orign = new Point(ctrlRect.Left - Left, ctrlRect.Top - Top); Rectangle viewRect = new Rectangle(orign, overlappedRect.Size); //g.SetClip(clipRect); //g.TranslateClip(-orign.X, -orign.Y); //g.RenderingOrigin = new Point(-orign.X, - orign.Y); using (PaintEventArgs args = new PaintEventArgs(g, clipRect)) { this.InvokePaintBackground(overlappedCtrl, args); this.InvokePaint(overlappedCtrl, args); } Region rgn = new Region(ctrlRect); rgn.Exclude(overlappedRect); overlappedCtrl.Invalidate(rgn, true); } }using System;using System.Collections.Generic;using System.Text;using System.Runtime.InteropServices;namespace GISUtilities{ public class AlphaBlend { // Methods public AlphaBlend() { } public void AlphaBlendNumber(IntPtr Handle, short Num) { AlphaBlend.SetLayeredWindowAttributes(Handle, 0, (byte)Num, 2); } public void AlphaBlendPercent(IntPtr Handle, short Percent) { AlphaBlend.SetLayeredWindowAttributes(Handle, 0, (byte)Math.Round((double)((((double)Percent) / 100) * 255)), 2); } public IntPtr FindApplicationWindow([Optional] string WindowClass /* = null */, [Optional] string WindowTitle /* = null */) { return AlphaBlend.FindWindow(WindowClass, WindowTitle); } [DllImport("user32", CharSet = CharSet.Auto, SetLastError = true)] private static extern IntPtr FindWindow(string lpClassName, string lpWindowName); [DllImport("user32", CharSet = CharSet.Auto, SetLastError = true)] private static extern int GetWindowLong(IntPtr Handle, int nIndex); public void ResetAlphaBlending(IntPtr Handle) { int num1 = AlphaBlend.GetWindowLong(Handle, -20); // The following line has me stumped, in VB, the const WS_EX_LAYERED breaks down to the value 524288 // Yet on the line below is the same number but increased by one, basically it should (boolean here) num1 and not WS_EX_LAYERED AlphaBlend.SetWindowLong(Handle, -20, num1 & -524289); } [DllImport("user32", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)] private static extern int SetLayeredWindowAttributes(IntPtr Handle, int crKey, byte bAlpha, int dwFlags); public void SetupAlphaBlending(IntPtr Handle) { int num1 = AlphaBlend.GetWindowLong(Handle, -20); num1 |= WS_EX_LAYERED; AlphaBlend.SetWindowLong(Handle, -20, num1); } [DllImport("user32", CharSet = CharSet.Auto, SetLastError = true)] private static extern int SetWindowLong(IntPtr Handle, int nIndex, int dwNewLong); // Fields private const short GWL_EXSTYLE = -20; private const short LWA_ALPHA = 2; private const short LWA_COLORKEY = 1; private const int WS_EX_LAYERED = 0x80000; }}