1

I have multiple GroupBoxes and Labels inside GroupBoxes etc.
I am trying to make a simple translucent Panel to overlay all Controls, but it just refuses to work properly.

I am using this custom Panel Control, but it does not cover Labels and other Controls: Labels show through it but you cannot click them. I don't know what kind of magic is this.

Does anybody have a solution to overlay and dim other controls?

using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;

public class ExtendedPanel : Panel
{
    private const int WS_EX_TRANSPARENT = 0x20;

    public ExtendedPanel() {
        SetStyle(ControlStyles.Opaque, true);
    }
    
    private int opacity = 50;

    [DefaultValue(50)]
    public int Opacity {
        get {
            return this.opacity;
        }
        set {
            if (value < 0 || value > 100)
                throw new ArgumentException("value must be between 0 and 100");
            this.opacity = value;
          }
      }

    protected override CreateParams CreateParams {
        get {
            CreateParams cp = base.CreateParams;
            cp.ExStyle = cp.ExStyle | WS_EX_TRANSPARENT;
            return cp;
        }
    }
    protected override void OnPaint(PaintEventArgs e) {
        using (var brush = new SolidBrush(Color.FromArgb(this.opacity * 255 / 100, this.BackColor))) {
            e.Graphics.FillRectangle(brush, this.Parent.ClientRectangle);
        }
        base.OnPaint(e);
    }
}
Jimi
  • 29,621
  • 8
  • 43
  • 61
Danijel
  • 817
  • 1
  • 17
  • 31

1 Answers1

1

Try out this transparent Label Custom Control.

It's similar to the Panel Control you were trying to build, but it's slight different than a Panel (ScrollableControl): a Panel generates a form of persistence when its background is drawn (doesn't matter whether you have set ControlStyles.Opaque = false, so it has no initial background) that causes the background (Color with Alpha or not) to draw over the previous layer.

The Label Control doesn't have this problem.
Drag it on a Container (Form or other child Containers) then stretch it over any other Control: all the controls it overlays will be drawn under it and the translucency is preserved.

  • You can overlay Controls inside other containers (GroupBox included) in a Form or inside child containers.
  • You cannot overlay a running ProgressBar (without tweaking the custom Control quite a bit).

See this similar implementation:
Translucent circular Control with text

Also the notes about the use of transparent Panels here:
Transparent Overlapping Circular Progress Bars (Custom Control)

This same effect cannot be achieved using a transparent Label or PictureBox (which you could also use to replace the Label in the code presented here).

The overlay Form Hans Passant linked in comments:
Draw semi transparent overlay image all over the windows form having some controls
works pretty well of course; this is an alternative way, probably easier to use at Design-Time, but clearly not useful in all situations (e.g., when you have to completely overlay a Form).


Note that I've change the range of Opacity property value from 0-100 to 0-255: I prefer it this way. Of course you can use whatever numeric range you prefer.

▶ You can overlay scrollable Controls, as a TextBox/RichTextBox, but you cannot let it scroll in any way, it will ruin the party (if you forget to/cannot also invalidate the translucent overlay quick enough).
▶ Also important: the lowest .Net version I test code against is .Net Framework 4.8 (which has changed/updated a lot of things in many departments)

using System.ComponentModel; using System.Drawing; using System.Runtime.InteropServices; using System.Windows.Forms;

[DesignerCategory("code")]
public class LabelTransparent : Label, IMessageFilter
{
    private const int WS_EX_TRANSPARENT = 0x0020;
    private const int WM_PAINT = 0x000F;
    private IntPtr m_TopLevelWindow = IntPtr.Zero;
    private int m_Opacity = 127;

    public LabelTransparent()
    {
        SetStyle(ControlStyles.Opaque | ControlStyles.Selectable |
                 ControlStyles.ResizeRedraw, true);
        SetStyle(ControlStyles.OptimizedDoubleBuffer, false);
    }

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

    public bool PreFilterMessage(ref Message m)
    {
        if (IsHandleCreated && m_TopLevelWindow != IntPtr.Zero && m.Msg == WM_PAINT) {
            var handle = m.HWnd;
            if (GetAncestor(handle, 3) != m_TopLevelWindow) return false;
            if (handle != this.Handle && handle != Parent?.Handle) {
                GetWindowRect(handle, out RECT rect);
                if (RectangleToScreen(ClientRectangle).IntersectsWith(rect.ToRectangle())) {
                    InvalidateOverlapped();
                    return true;
                }
            }
        }
        return false;
    }

    protected override void OnHandleCreated(EventArgs e)
    {
        if (!DesignMode) Application.AddMessageFilter(this);
        m_TopLevelWindow = FindForm().Handle;
        base.OnHandleCreated(e);
    }

    protected override void OnHandleDestroyed(EventArgs e)
    {
        if (!DesignMode) Application.RemoveMessageFilter(this);
        m_TopLevelWindow = IntPtr.Zero;
        base.OnHandleDestroyed(e);
    }

    protected override void OnLayout(LayoutEventArgs e)
    {
        base.OnLayout(e);
        AutoSize = false;
        base.Text = string.Empty;
    }

    public new Color BackColor {
        get => base.BackColor;
        set {
            if (base.BackColor == value) return;
            base.BackColor = value;
            InvalidateOverlapped();
        }
    }

    [DefaultValue(127)]
    public int Opacity {
        get => m_Opacity;
        set {
            if (m_Opacity == value) return;
            m_Opacity = Math.Max(Math.Min(value, 255), 0);
            InvalidateOverlapped();
        }
    }

    protected override void OnPaint(PaintEventArgs e)
    {
        using (var brush = new SolidBrush(Color.FromArgb(m_Opacity, BackColor))) {
            e.Graphics.FillRectangle(brush, ClientRectangle);
        }
    }

    private void InvalidateOverlapped() {
        if (Parent == null || Parent.Handle == IntPtr.Zero) return;
        Parent.Invalidate(Bounds, true);
        Parent.Update();
    }

    [DllImport("user32.dll")]
    private static extern IntPtr GetAncestor(IntPtr hwnd, uint gaFlags);

    [DllImport("User32.dll", SetLastError = true)]
    private static extern bool GetWindowRect(IntPtr hWnd, out RECT rc);


    [StructLayout(LayoutKind.Sequential)]
    private struct RECT
    {
        public int Left;
        public int Top;
        public int Right;
        public int Bottom;

        public RECT(int x, int y, int width, int height) {
            Left = x;
            Top = y;
            Right = x + width;
            Bottom = y + height;
        }
        public Rectangle ToRectangle() => Rectangle.FromLTRB(Left, Top, Right, Bottom);
    }
}
Jimi
  • 29,621
  • 8
  • 43
  • 61