繁体   English   中英

在 Windows 上绘制 C#

[英]C# drawing on windows

我正在尝试在现有窗口上绘制一些线条和矩形。 我发现以下代码可以在桌面上绘制,效果非常好。

class Drawing {
[DLLImport("User32.dll")] 
static extern IntPtr GetDC(IntPtr hWnd);       

    public static void draw(Rectangle r, Brush b, IntPtr hWnd) {
        using(Graphics g = Graphics.FromHdc(hwnd)) {
            g.DrawRectangle(b, r);
        }
    }
}

Drawing.draw(new Rectangle(0, 0, 40, 80), Brushes.Red, Drawing.GetDC(IntPtr.Zero));

这是我如何修改它以在特定窗口上绘制的代码。

class Drawing {
    public static IntPtr WinGetHandle(string wName) {
        foreach (Process pList in Process.GetProcesses())
            if (pList.MainWindowTitle.Contains(wName))
                return pList.MainWindowHandle;

        return IntPtr.Zero;
    }

    public static void draw(Rectangle r, Brush b, IntPtr hwnd) {
        using(Graphics g = Graphics.FromHwnd(hwnd)) {
            g.DrawRectangle(p, r);
        }
    }
}

Drawing.draw(new Rectangle(0, 0, 40, 80), Brushes.Red, Drawing.WinGetHandle("DrawingWindow"));

这段代码什么也没发生,在某些情况下,它会抛出 OutOfMemoryException 并立即崩溃。 在互联网上,我发现了获取窗口句柄然后在窗口上绘制的其他一些变体,但大多数崩溃都出现相同的异常或什么也不做。 我还循环尝试了它,因为我认为该应用程序可能会在每一帧中过度绘制。

所以我的问题是有人知道我如何解决这个问题或遇到同样的问题,而另一种方法可以做到这一点。

正如评论中所说,您不能直接在屏幕上绘图。 您可以做的是构建一些“叠加”窗口( 透明和点击)并在其上绘制。

这是一个 C# 控制台应用程序示例,它演示了这一点,还使用了 UI 自动化来跟踪打开的窗口并在它们周围绘制一个黄色矩形。

static class Program
{
    [STAThread]
    static void Main()
    {
        var overlay = new Overlay();

        // track windows open (requires WindowsBase, UIAutomationTypes, UIAutomationClient)
        Automation.AddAutomationEventHandler(WindowPattern.WindowOpenedEvent, AutomationElement.RootElement, TreeScope.Subtree, (s, e) =>
        {
            var element = (AutomationElement)s;
            if (element.Current.ProcessId != Process.GetCurrentProcess().Id)
            {
                Console.WriteLine("Added window '" + element.Current.Name + "'");
                overlay.AddTrackedWindow(element);

                // track window close
                Automation.AddAutomationEventHandler(WindowPattern.WindowClosedEvent, element, TreeScope.Element, (s2, e2) =>
                {
                    overlay.RemoveTrackedWindow(element);
                });
            }
        });

        Application.Run(overlay);
    }
}

// adapted from https://stackoverflow.com/questions/11077236/transparent-window-layer-that-is-click-through-and-always-stays-on-top
public class Overlay : Form // standard Windows Form
{
    private readonly HashSet<AutomationElement> _windows = new HashSet<AutomationElement>();

    public Overlay()
    {
        TopMost = true;
        FormBorderStyle = FormBorderStyle.None;
        WindowState = FormWindowState.Maximized;
        MaximizeBox = false;
        MinimizeBox = false;
        ShowInTaskbar = false;
        BackColor = Color.White;
        TransparencyKey = BackColor;
    }

    protected override CreateParams CreateParams
    {
        get
        {
            var cp = base.CreateParams;
            const int WS_EX_TRANSPARENT = 0x20;
            const int WS_EX_LAYERED = 0x80000;
            const int WS_EX_NOACTIVATE = 0x8000000;
            cp.ExStyle |= WS_EX_LAYERED | WS_EX_TRANSPARENT | WS_EX_NOACTIVATE;
            return cp;
        }
    }

    protected override void OnPaint(PaintEventArgs e)
    {
        base.OnPaint(e);

        foreach (var window in _windows.ToArray())
        {
            Rect rect;
            try
            {
                rect = window.Current.BoundingRectangle;
            }
            catch
            {
                // error, window's gone
                _windows.Remove(window);
                continue;
            }

            // draw a yellow rectangle around window
            using (var pen = new Pen(Color.Yellow, 2))
            {
                e.Graphics.DrawRectangle(pen, (float)rect.X, (float)rect.Y, (float)rect.Width, (float)rect.Height);
            }
        }
    }

    // ensure we call Invalidate on UI thread
    private void InvokeInvalidate() => BeginInvoke((Action)(() => { Invalidate(); }));

    public void RemoveTrackedWindow(AutomationElement element)
    {
        _windows.Remove(element);
        InvokeInvalidate();
    }

    public void AddTrackedWindow(AutomationElement element)
    {
        _windows.Add(element);
        InvokeInvalidate();

        // follow target window position
        Automation.AddAutomationPropertyChangedEventHandler(element, TreeScope.Element, (s, e) =>
        {
            InvokeInvalidate();
        }, AutomationElement.BoundingRectangleProperty);
    }
}

要测试它,运行它,然后打开记事本。 这是你应该看到的:

在此处输入图片说明

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM