[英]How to create a transparent control which works when on top of other controls?
I have a control (derived from System.Windows.Forms.Control) which needs to be transparent in some areas. 我有一个控件(派生自System.Windows.Forms.Control),在某些方面需要透明。 I have implemented this by using SetStyle():
我已经使用SetStyle()实现了这个:
public TransparentControl()
{
SetStyle(ControlStyles.SupportsTransparentBackColor, true);
this.BackColor = Color.Transparent.
}
Now, this works if there are no controls between the form and the transparent control. 现在,如果表单和透明控件之间没有控件,则此方法有效。 However, if there happens to be another control below the transparent control (which is the use case here), it does not work.
但是,如果在透明控件(这里是用例)下面碰巧有另一个控件,则它不起作用。 The intermediate control is not draw, but the form below does show through.
中间控件不是绘制,但下面的表格确实显示。 I can get the effect that I need by overriding CreateParams and setting the WS_EX_TRANSPARENT flasg like so:
我可以通过重写CreateParams并设置WS_EX_TRANSPARENT flashg来获得我需要的效果:
protected override CreateParams CreateParams
{
get
{
CreateParams cp = base.CreateParams;
cp.ExStyle |= 0x20; // WS_EX_TRANSPARENT
return cp;
}
}
The problem here is that it really slows down the painting of the control. 这里的问题是它确实减慢了控件的绘制速度。 The control is already double buffered, so nothing to do there.
控件已经是双缓冲的,所以没有什么可做的。 The performance hit is so bad that it is unacceptable.
性能受到的打击非常糟糕,这是不可接受的。 Has anyone else encountered this problem?
还有其他人遇到过这个问题吗? All of the resources that I can find suggest using method #1, but again, that does not work in my case.
我可以找到的所有资源建议使用方法#1,但同样,这在我的情况下不起作用。
EDIT: I should note that I do have a workaround. 编辑:我应该注意到我确实有一个解决方法。 The child (transparent) controls could simply draw themselves onto the parent's Graphics object, but it is really ungly and I don't like the solution at all (though it may be all I have).
子(透明)控件可以简单地将自己绘制到父对象的Graphics对象上,但它真的很笨,我根本不喜欢这个解决方案(虽然它可能就是我所拥有的)。
EDIT2: So, following the advice that I got on how transparency works in .NET, I have implemented the IContainer interface in my user control which contains the transparent controls. EDIT2:所以,按照我在.NET中透明度如何工作的建议,我在我的用户控件中实现了IContainer接口,其中包含透明控件。 I have created a class which implements ISite, I add my child controls to the Components collection of the UserControl, the Container property lines up correctly in the debugger, but I still do not get a transparency effect.
我创建了一个实现ISite的类,我将我的子控件添加到UserControl的Components集合中,Container属性在调试器中正确排列,但我仍然没有获得透明效果。 Does anyone have any ideas?
有没有人有任何想法?
This is just a simple thing I cooked up.. The only issue I've found is that it doesn't update when the intersecting controls are updated.. 这只是我编写的一个简单的事情..我发现的唯一问题是当交叉控件更新时它不会更新..
It works by drawing a control that's behind/intersects with the current control to a bitmap, then drawing that bitmap to the current control.. 它的工作原理是将一个位于当前控件后面/与之交叉的控件绘制到位图,然后将该位图绘制到当前控件。
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();
}
}
Transparent controls in DotNet are implemented by having the transparent control's container draw itself in the transparent control's window and then having the transparent control draw itself. DotNet中的透明控件是通过让透明控件的容器在透明控件的窗口中绘制自然然后让透明控件自行绘制来实现的。 This process doesn't take into account the possibility of overlapping controls.
此过程未考虑重叠控制的可能性。 So you will need to use some sort of work-around to make it work.
因此,您需要使用某种解决方法来使其工作。
In some cases I've had success with complex nesting, but that's mostly only good for quick-and-dirty layering of bitmaps, and it doesn't solve any issues with partially overlapping controls. 在某些情况下,我已经成功进行了复杂的嵌套,但这主要仅适用于快速和脏的位图分层,并且它不能解决部分重叠控件的任何问题。
I found out that the modifications below make things a bit faster: 我发现下面的修改使事情变得更快:
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();
}
I also think that using this.Width
/ this.Height
instead of Parent.Width
/ Parent.Height
would be even faster, but I didn't have time to tinker with it. 我也认为使用
this.Width
/ this.Height
而不是Parent.Width
/ Parent.Height
会更快,但我没有时间修补它。
Drawing the siblings under the control is possible, but it's ugly. 在控制下绘制兄弟姐妹是可能的,但它很难看。 The code below works reasonably well for me, it expands on the code given in the link in Ed S.'
下面的代码对我来说效果很好,它扩展了Ed S.链接中给出的代码。 answer .
回答 。
Possible pitfalls: 可能的陷阱:
DrawToBitmap
was introduced with .net 2.0, so don't expect it to work with anything older than that. DrawToBitmap
是使用.net 2.0引入的,所以不要指望它可以使用比这更早的东西。 But even then something like this may be possible by sending WM_PRINT to the sibling control; WS_EX_TRANSPARENT
since according to msdn that window style fiddles with the painting order. WS_EX_TRANSPARENT
,则可能也会出现问题,因为根据msdn ,窗口样式会与绘制顺序WS_EX_TRANSPARENT
。 I haven't got any controls that use this style so I can't tell. Here's the code: 这是代码:
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);
}
}
I decided to simply paint the parent under my child controls manually. 我决定手动在父控件下绘制父级。 Here is a good article.
这是一篇好文章。
Some suggestions (apologies for the VB code). 一些建议(对VB代码道歉)。
Try to avoid painting the background: 尽量避免绘制背景:
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
Don't call the controls base paint method: 不要调用控件基础绘制方法:
Protected Overrides Sub OnPaint(ByVal e As System.Windows.Forms.PaintEventArgs)
'MyBase.OnPaint(e) - comment out - do not call
End Sub
This does the trick, at least it has for me: 这就是诀窍,至少它对我有用:
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));
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.