如何创建一个在其他控件之上工作的透明控件?

我有一个控件(派生自System.Windows.Forms.Control),在某些方面需要透明。 我已经使用SetStyle()实现了这个:

public TransparentControl() { SetStyle(ControlStyles.SupportsTransparentBackColor, true); this.BackColor = Color.Transparent. } 

现在,如果表单和透明控件之间没有控件,则此方法有效。 但是,如果在透明控件(这里是用例)下面碰巧有另一个控件,则它不起作用。 中间控件不是绘制,但下面的表格确实显示。 我可以通过重写CreateParams并设置WS_EX_TRANSPARENT flashg来获得我需要的效果:

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

这里的问题是它确实减慢了控件的绘制速度。 控件已经是双缓冲的,所以没什么可做的。 性能受到的打击非常糟糕,这是不可接受的。 还有其他人遇到过这个问题吗? 我能找到的所有资源建议使用方法#1,但同样,这在我的情况下不起作用。

编辑:我应该注意到我确实有一个解决方法。 子(透明)控件可以简单地将自己绘制到父对象的Graphics对象上,但它实际上是笨拙的,我根本不喜欢这个解决方案(虽然它可能就是我所拥有的)。

编辑2:所以,根据我对.NET透明度如何工作的建议,我在我的用户控件中实现了IContainer接口,其中包含透明控件。 我创建了一个实现ISite的类,我将我的子控件添加到UserControl的Components集合中,Container属性在调试器中正确排列,但我仍然没有获得透明效果。 有没有人有任何想法?

这只是我编写的一个简单的事情..我发现的唯一问题是当交叉控件更新时它不会更新..

它的工作原理是将一个位于当前控件后面/与之交叉的控件绘制到位图,然后将该位图绘制到当前控件。

 protected override void OnPaint(PaintEventArgs e) { if (Parent != null) { Bitmap behind = new Bitmap(Parent.Width, Parent.Height); foreach (Control c in Parent.Controls) if (c.Bounds.IntersectsWith(this.Bounds) & c != this) c.DrawToBitmap(behind, c.Bounds); e.Graphics.DrawImage(behind, -Left, -Top); behind.Dispose(); } } 

DotNet中的透明控件是通过让透明控件的容器在透明控件的窗口中绘制自然然后让透明控件自行绘制来实现的。 此过程未考虑重叠控制的可能性。 因此,您需要使用某种解决方法来使其工作。

在某些情况下,我已经成功进行了复杂的嵌套,但这主要仅适用于位图的快速分层,并且它不能解决部分重叠控件的任何问题。

我发现下面的修改使事情变得更快:

 if((this.BackColor == Color.Transparent) && (Parent != null)) { Bitmap behind = new Bitmap(Parent.Width, Parent.Height); foreach(Control c in Parent.Controls) { if(c != this && c.Bounds.IntersectsWith(this.Bounds)) { c.DrawToBitmap(behind, c.Bounds); } } e.Graphics.DrawImage(behind, -Left, -Top); behind.Dispose(); } 

我也认为使用this.Width / this.Height而不是Parent.Width / Parent.Height会更快,但我没有时间修补它。

在控制下绘制兄弟姐妹是可能的,但它很难看。 下面的代码对我来说效果很好,它扩展了Ed S.链接中给出的代码。 回答 。

可能的陷阱:

  • DrawToBitmap是使用.net 2.0引入的,所以不要指望它可以使用比这更早的东西。 但即使这样,通过将WM_PRINT发送到兄弟控件也可以实现这样的function。 AFAIK就是DrawToBitmap内部的function。
  • 如果您控制下的控件使用WS_EX_TRANSPARENT ,则可能也会出现问题,因为根据msdn ,窗口样式会与绘制顺序WS_EX_TRANSPARENT 。 我没有任何使用这种风格的控件,所以我无法分辨。
  • 我正在运行带有VS2010的XP SP3,因此这种方法在Vista或W7上可能会有其他问题。

这是代码:

 if (Parent != null) { float tx = -Left, ty = -Top; // make adjustments to tx and ty here if your control // has a non-client area, borders or similar e.Graphics.TranslateTransform(tx, ty); using (PaintEventArgs pea = new PaintEventArgs(e.Graphics,e.ClipRectangle)) { InvokePaintBackground(Parent, pea); InvokePaint(Parent, pea); } e.Graphics.TranslateTransform(-tx, -ty); // loop through children of parent which are under ourselves int start = Parent.Controls.GetChildIndex(this); Rectangle rect = new Rectangle(Left, Top, Width, Height); for (int i = Parent.Controls.Count - 1; i > start; i--) { Control c = Parent.Controls[i]; // skip ... // ... invisible controls // ... or controls that have zero width/height (Autosize Labels without content!) // ... or controls that don't intersect with ourselves if (!c.Visible || c.Width == 0 || c.Height == 0 || !rect.IntersectsWith(new Rectangle(c.Left, c.Top, c.Width, c.Height))) continue; using (Bitmap b = new Bitmap(c.Width, c.Height, e.Graphics)) { c.DrawToBitmap(b, new Rectangle(0, 0, c.Width, c.Height)); tx = c.Left - Left; ty = c.Top - Top; // make adjustments to tx and ty here if your control // has a non-client area, borders or similar e.Graphics.TranslateTransform(tx, ty); e.Graphics.DrawImageUnscaled(b, new Point(0, 0)); e.Graphics.TranslateTransform(-tx, -ty); } } 

我决定手动在父级控件下绘制父级。 这是一篇好文章。

一些建议。 为VB代码道歉。

尽量避免画背景:

 Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message) If m.Msg = &H14 Then Return End If MyBase.WndProc(m) End Sub 

 Protected Overrides Sub OnPaintBackground(ByVal pevent As System.Windows.Forms.PaintEventArgs) Return End Sub 

不要调用控件基础绘制方法:

 Protected Overrides Sub OnPaint(ByVal e As System.Windows.Forms.PaintEventArgs) 'MyBase.OnPaint(e) - comment out - do not call End Sub 

这就是诀窍,至少它对我有用:

 protected override void OnPaintBackground(PaintEventArgs e) { //base.OnPaintBackground(e); this.CreateGraphics().DrawRectangle(new Pen(Color.Transparent, 1), new Rectangle(0, 0, this.Size.Width, this.Size.Height)); }