创建半透明面板/控件。 有一种万无一失的方式吗?

我正在尝试创建一个从System.Windows.Forms.Panel派生的半透明控件。 [编辑:基本上我想要实现的是这个]:

在此处输入图像描述

我已经浏览了大量的网络文章和SO问题,并提出了这个问题:

 class SeeThroughPanel : Panel { public SeeThroughPanel() { } protected override CreateParams CreateParams { get { var cp = base.CreateParams; cp.ExStyle |= 0x00000020; return cp; } } protected override void OnPaint(PaintEventArgs e) { //base.OnPaint(e); e.Graphics.FillRectangle(new SolidBrush(Color.FromArgb(50, 0, 0, 0)), this.ClientRectangle); } } 

结果如此(请注意,此代码也不会使其真正半透明。按钮显示为全彩色,与上图不同):

自定义控件按预期显示透明度。

但是,只要我做了一些导致重新绘制半透明面板边界内任何其他控件的东西,它就会被透支(在这种情况下,我已经滚动了底层控件):

自定义控件错误地重新绘制。

所以我的问题是,我该如何避免这种情况? 必须有某种方式。 毕竟,Web浏览器始终处理这种渲染。

我现在做了一些测试。

你将几个问题混合成一个,所以让我们分开:

  • 没有WebBrowser是使用Winforms用C#编写的。 所以这不是为什么必须这样做的理由。

  • 您的按钮几乎肯定不在Form控件集合中。 您需要将其编写为脚本或使用一个小UI技巧(*)将其放在另一个控件上而不将其添加到其中。

  • 滚动时,您将看到滚动控件报告的表面。

这是两个截图:

这是在启动后:

screenshot1

..这是在我向右滚动一点之后:

screenshot1

我使用此代码来确保Z顺序是正确的:

 button1.Parent = this; panel1.BringToFront(); button1.BringToFront(); seeThroughPanel1.BringToFront(); 

注意按钮的空间是如何保留的; 这显示了旧表面的使用方式。

要解决这个问题,您必须获取当前的Form surfacse(可能是Control.DrawToBitmap),然后使用右侧部分绘制“半透明”面板。 当然,在捕获表单的当前表面之前必须隐藏它。

在我所有的可怕想法中,这似乎是最糟糕的……

*诀窍是用键盘而不是鼠标将其移到容器上; 有了这个技巧,它只是移动而不会更新其父容器。 将控件放在TabControl的选项卡顶部也很有用……但是我太懒了,所以我把它编码了..

看看这篇文章: 创建透明的Windows窗体控件。

面板类来自链接:

Windows窗体控件的透明度function还有很多不足之处,而且是一种明显的软糖。 控件不是真正透明的,它只是假装通过查看它的父控件的背景并在OnPaintBackground方法期间将图像或背景的适当部分复制到它自己的表面上。

这意味着在同一父节点上放置在另一个节点之上的“透明”控件实际上会掩盖其他子节点控件。 图1显示了这种效果。

在此处输入图像描述

窗体右侧的面板控件遮盖了PictureBox控件并仅显示父窗体的背景。

为了实现真正透明的控制,我们需要做一些事情。 首先,有必要通过赋予WS_EX_TRANSPARENT样式来改变窗口的行为。 这是通过重写CreateParams属性来实现的,以便在实例化控件时包含正确的窗口样式。 下面的列表显示了此属性覆盖。

 protected override CreateParams CreateParams { get { CreateParams cp=base.CreateParams; cp.ExStyle|=0x00000020; //WS_EX_TRANSPARENT return cp; } } 

我们需要做的第二件事是在需要更新图形时使控件的父级无效,而不是控件本身。 这可以确保在我们需要执行自己的图形输出之前,控件背后的任何内容都会被绘制。 为此,需要执行如下列表中所示的例程。

 protected void InvalidateEx() { if(Parent==null) return; Rectangle rc=new Rectangle(this.Location,this.Size); Parent.Invalidate(rc,true); } 

最后,我们需要确保背景绘制例程不会通过删除OnPaintBackground方法来破坏最近重新绘制的父表单内容。

 protected override void OnPaintBackground(PaintEventArgs pevent) { //do not allow the background to be painted } 

现在,控件已准备好进行其余修改。 此时需要注意的是,透明控件不适合使用标准SetStyle方法进行双缓冲。 提供给代码的内存位图具有不透明的背景,并且不允许显示精心保留的父像素。

为了完成本文,下面的清单中显示了一个简单的控件,除了在其表面上绘制移动椭圆之外什么都不做。

 using System; using System.Drawing; using System.Windows.Forms; internal class SeeThroughPanel : Panel { public SeeThroughPanel() { } protected void TickHandler(object sender, EventArgs e) { this.InvalidateEx(); } protected override CreateParams CreateParams { get { CreateParams cp = base.CreateParams; cp.ExStyle |= 0x00000020; //WS_EX_TRANSPARENT return cp; } } protected void InvalidateEx() { if (Parent == null) { return; } Rectangle rc = new Rectangle(this.Location, this.Size); Parent.Invalidate(rc, true); } protected override void OnPaintBackground(PaintEventArgs pevent) { } private Random r = new Random(); protected override void OnPaint(PaintEventArgs e) { e.Graphics.FillRectangle(new SolidBrush(Color.FromArgb(128, 0, 0, 0)), this.ClientRectangle); } } 

在此处输入图像描述