winforms 如何创建一个透明的控件,它可以在其他控件之上工作?

k2arahey  于 8个月前  发布在  其他
关注(0)|答案(7)|浏览(94)

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

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

现在,如果窗体和透明控件之间没有控件,则此方法有效。但是,如果在透明控件下面碰巧有另一个控件(这是这里的用例),它就不起作用。中间控件不是draw,但下面的表单确实显示出来了。我可以通过覆盖WARNING Params并设置WS_EX_TRANSPARENT flasg来获得所需的效果,如下所示:

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属性在调试器中正确排列,但我仍然没有获得透明效果。有人有什么想法吗?

7hiiyaii

7hiiyaii1#

这只是我做的一个简单的事情。我发现的唯一问题是,当交叉控件更新时,它不会更新。
它的工作原理是将当前控件后面/与当前控件相交的控件绘制到位图中,然后将该位图绘制到当前控件。

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();
    }
}
qltillow

qltillow2#

DotNet中的透明控件是通过让透明控件的容器在透明控件的窗口中绘制自身,然后让透明控件绘制自身来实现的。这个过程没有考虑到重叠控制的可能性。因此,您需要使用某种变通方法来使其工作。
在某些情况下,我已经成功地处理了复杂的嵌套,但这主要只适用于位图的快速分层,并且不能解决部分重叠控件的任何问题。

dohp0rv5

dohp0rv53#

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

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

roejwanj

roejwanj4#

将兄弟姐妹置于控制之下是可能的,但它很丑。下面的代码对我来说工作得相当好,它扩展了Ed S.' answer中链接中给出的代码。
可能的陷阱:

  • DrawToBitmap是在.net 2.0中引入的,所以不要指望它能与任何更老的东西一起工作。但即使这样,通过向兄弟控件发送WM_PRINT,也可以实现类似的功能;这就是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);
        }
}
q5lcpyga

q5lcpyga5#

我决定简单地在我的子控件下手动绘制父控件。Here is a good article.

9fkzdhlc

9fkzdhlc6#

一些建议(为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
tkclm6bt

tkclm6bt7#

这是一个诀窍,至少对我来说是这样的:

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

相关问题