简体   繁体   English

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

[英]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; 但即使这样,通过将WM_PRINT发送到兄弟控件也可以实现这样的功能。 AFAIK that's what DrawToBitmap does internally. AFAIK就是DrawToBitmap内部所做的事情。
  • It may also have problems if you have controls under your control that make use of 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. 我没有任何使用这种风格的控件,所以我无法分辨。
  • I'm running XP SP3 with VS2010, therefore this approach may have additional problems on Vista or W7. 我正在运行带有VS2010的XP SP3,因此这种方法在Vista或W7上可能会有其他问题。

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.

相关问题 控件分层时如何在Windows窗体中创建透明控件 - How to create transparent controls in Windows Forms when controls are layered 如何将ListView控件置于WPF中的其他控件之上 - How I can bring ListView control in top of other controls in WPF 对其他控件上方的控件高度进行动画处理 - Animate control height over the top of other controls 如何显示控件下面的控件(透明)没有覆盖BackColor? - How to display a control which (transparent) BackColor doesn't cover below controls? 如何在其中包含许多控件的控件上绘制图形? (Z顺序最高) - How can i draw on top of an control which has many controls in it? (Z-order top) 在视频顶部具有透明背景的控件 - Controls with transparent background on top of video 如何确定一个控件是否是其他控件的容器 - How to determine if a Control is a Container for other Controls 如何在Windows Mobile上使用背景图像使标签和其他控件透明? - How to make label & other controls transparent with background image on windows mobile? 我怎样才能使窗体背景透明而不能使其他控件透明 - how can I make just the Form background transparent but not other controls 如何在C#中的透明表单上创建半透明控件 - How create a semi transparent control on a transparent form in C#
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM