Search code examples
c#.netwinformssystem.drawing

How to draw on top of the right-click menu in .NET/C#?


I'm developing an assistive technology application (in C#) that overlays information on top of the currently open window. It detects clickable elements, and labels them.

To do this, I'm currently creating a borderless, transparent window with TopMost set to "true", and drawing the labels on that. This means there is always a window hovering in front of the current application, on which I can draw the labels.

The problem is, this window doesn't cover the right-click menu - only other windows. When the user right-clicks, the context menu is drawn above the overlay.

I need to be able to label elements in the right-click menu, but I can't draw on top of it with the current implementation. Does anybody know of a solution?

Edit: This is the relevant code for drawing the overlay. I've set the form options in the form designer, not in the code explicity, so I'm not sure how much it will help. I've removed the code not related to drawing, or the form itself:

public partial class OverlayForm : Form
{
    public OverlayForm()
    {

    }

    protected override void OnPaint(PaintEventArgs eventArgs)
    {
        base.OnPaint(eventArgs);
        Graphics graphics = eventArgs.Graphics;
        Brush brush = new SolidBrush(this.labelColor);
        foreach (ClickableElement element in this.elements)
        {
            Region currentRegion = element.region;
            graphics.FillRegion(brush, currentRegion);
        }
    }    
}

Solution

  • I've found a bit of a hack solution.

    By periodically bringing the overlay window to the front, I can bring it to the top of the Z-order where it covers the right-click menu. It's not perfect, but it does work. This is the core concept, with multithreading stripped out:

    public class OverlayManager
    {
        OverlayForm overlay;
    
        public OverlayManager() 
        {
            this.overlay = new OverlayForm();
            this.overlay.Show();
            this.RepeatedlyBringToFront();
        }
    
        private void RepeatedlyBringToFront()
        {
            while (true)
            {
                this.overlay.BringToFront();
                Thread.Sleep(50);
            }
        }
    }
    

    (This assumes "OverlayForm" is a form with the relevant flags set in the form designer - i.e. "Always on Top," "Transparency Key," "Borderless," "Maximised," etc.)

    I've only tested this on Windows 8.1. I don't know if the Z-order behaves this way on other versions.