当前位置: 代码迷 >> .NET组件控件 >> 在 .NET 中怎么实现真正的透明控件
  详细解决方案

在 .NET 中怎么实现真正的透明控件

热度:2897   发布时间:2013-02-25 00:00:00.0
在 .NET 中如何实现真正的透明控件
.NET 里的控件背景色所谓的“透明背景”是通过绘制父控件的背景实现的,并不是真正的透明效果。
因此,当多个具有透明背景色的同级控件重叠时,Z 顺序位于前面的控件会遮挡住 Z 顺序小于它的控件。

现在我想要做一个能够真正实现透明背景的控件,思路如下:

1、模仿微软的做法,把和自己相交的控件的部分绘制为背景色

或者

2、使用传说中的 SetLayeredWindowAttribute 方法(未尝试过)。

对于方法1,在实施的时候出现了一些问题。

我新建了控件,继承于 Panel ,重写了控件的 OnPaitBackground 方法,代码如下:
C# code
        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);            }        }


上面这段代码的没有达到实际的效果,而是这样的怪异效果:
·如果控件和别的控件相交,原先控件的背景没了(如果不和其它控件相交,则正常)
·相交区域绘制的位置总是不正确(我以及多次调整过剪辑区域的位置,均没达到正确的效果)
·控件的文本总是绘制在不正确的位置上。
·如果控件 A 和控件 B 有相交的区域,现在移动控件 B,控件 B 的背景会被刷新,但是控件 A 的背景不被刷新。

哪位高手帮看一下代码出了什么问题,或者给出一个其它的解决方案,谢了~


------解决方案--------------------------------------------------------
LZ可以送10分技术分给我吗?急用`谢谢啦`好心有好报!
------解决方案--------------------------------------------------------
用以下类可以搞定。
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;
}
}

------解决方案--------------------------------------------------------
C# code
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;    }}
  相关解决方案