19

我有一个控件(派生自 System.Windows.Forms.Control),它在某些区域需要是透明的。我已经通过使用 SetStyle() 实现了这一点:

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

现在,如果窗体和透明控件之间没有控件,这将起作用。但是,如果在透明控件下方碰巧有另一个控件(这是这里的用例),它就不起作用。中间控件不是绘制的,但下面的表格确实显示出来了。我可以通过覆盖 CreateParams 并像这样设置 WS_EX_TRANSPARENT 标志来获得所需的效果:

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

这里的问题是它确实减慢了控件的绘制速度。该控件已经是双缓冲的,因此无事可做。性能影响是如此糟糕,以至于无法接受。有没有其他人遇到过这个问题?我能找到的所有资源都建议使用方法#1,但同样,这在我的情况下不起作用。

编辑:我应该注意我确实有一个解决方法。子(透明)控件可以简单地将自己绘制到父图形对象上,但它真的很丑陋,我根本不喜欢这个解决方案(尽管它可能是我所拥有的全部)。

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

4

7 回答 7

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();
    }
}
于 2012-05-19T07:55:48.740 回答
6

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

在某些情况下,我在复杂的嵌套方面取得了成功,但这主要只适用于位图的快速和肮脏的分层,并且它不能解决部分重叠控件的任何问题。

于 2009-02-26T22:10:19.920 回答
5

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

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会更快,但我没有时间去修改它。

于 2012-09-11T19:51:06.463 回答
4

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

可能的陷阱:

  • DrawToBitmap是在 .net 2.0 中引入的,所以不要指望它适用于比这更早的任何东西。但即便如此,通过将 WM_PRINT 发送到同级控件也可以实现这样的事情;AFAIK 这就是 DrawToBitmap 在内部所做的。
  • 如果您的控件在您的控制下使用,它也可能会出现问题,WS_EX_TRANSPARENT因为根据msdn的说法,窗口样式会影响绘画顺序。我没有任何使用这种风格的控件,所以我不知道。
  • 我在 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);
        }
}
于 2011-05-17T09:32:56.507 回答
3

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

于 2009-02-27T02:05:48.480 回答
2

一些建议(为 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
于 2009-02-26T22:23:24.647 回答
-2

这可以解决问题,至少它对我有用:

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));
}
于 2013-08-08T23:47:51.050 回答