Ed *_* S. 19 c# transparent-control winforms
我有一个控件(派生自System.Windows.Forms.Control),在某些方面需要透明.我已经使用SetStyle()实现了这个:
public TransparentControl()
{
SetStyle(ControlStyles.SupportsTransparentBackColor, true);
this.BackColor = Color.Transparent.
}
Run Code Online (Sandbox Code Playgroud)
现在,如果表单和透明控件之间没有控件,则此方法有效.但是,如果在透明控件(这里是用例)下面碰巧有另一个控件,则它不起作用.中间控件不是绘制,但下面的表格确实显示.我可以通过重写CreateParams并设置WS_EX_TRANSPARENT flashg来获得我需要的效果:
protected override CreateParams CreateParams
{
get
{
CreateParams cp = base.CreateParams;
cp.ExStyle |= 0x20; // WS_EX_TRANSPARENT
return cp;
}
}
Run Code Online (Sandbox Code Playgroud)
这里的问题是它确实减慢了控件的绘制速度.控件已经是双缓冲的,所以没有什么可做的.性能受到的打击非常糟糕,这是不可接受的.还有其他人遇到过这个问题吗?我可以找到的所有资源建议使用方法#1,但同样,这在我的情况下不起作用.
编辑:我应该注意到我确实有一个解决方法.子(透明)控件可以简单地将自己绘制到父对象的Graphics对象上,但它真的很笨,我根本不喜欢这个解决方案(虽然它可能就是我所拥有的).
EDIT2:所以,按照我在.NET中透明度如何工作的建议,我在我的用户控件中实现了IContainer接口,其中包含透明控件.我创建了一个实现ISite的类,我将我的子控件添加到UserControl的Components集合中,Container属性在调试器中正确排列,但我仍然没有获得透明效果.有没有人有任何想法?
小智 9
这只是我编写的一个简单的事情..我发现的唯一问题是当交叉控件更新时它不会更新..
它的工作原理是将一个位于当前控件后面/与之交叉的控件绘制到位图,然后将该位图绘制到当前控件.
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();
}
}
Run Code Online (Sandbox Code Playgroud)
DotNet中的透明控件是通过让透明控件的容器在透明控件的窗口中绘制自然然后让透明控件自行绘制来实现的.此过程未考虑重叠控制的可能性.因此,您需要使用某种解决方法来使其工作.
在某些情况下,我已经成功进行了复杂的嵌套,但这主要仅适用于快速和脏的位图分层,并且它不能解决部分重叠控件的任何问题.
将兄弟姐妹置于控制之下是可能的,但它很丑。下面的代码对我来说效果相当好,它扩展了Ed S 中链接中给出的代码。回答。
可能的陷阱:
DrawToBitmap
是随 .net 2.0 引入的,因此不要指望它能与比 .net 2.0 更旧的版本一起工作。但即便如此,通过向同级控件发送 WM_PRINT 也可能实现类似的操作;AFAIK 这就是 DrawToBitmap 内部所做的事情。WS_EX_TRANSPARENT
因为根据msdn 的说法,窗口样式会干扰绘制顺序。我没有任何使用这种风格的控件,所以我无法判断。这是代码:
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);
}
}
Run Code Online (Sandbox Code Playgroud)
我发现下面的修改使事情变得更快:
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();
}
Run Code Online (Sandbox Code Playgroud)
我也认为使用this.Width
/ this.Height
而不是Parent.Width
/ Parent.Height
会更快,但我没有时间修补它.