c# - 在窗体区域外绘图时如何重绘

标签 c# windows winforms winapi graphics

我正在编写一个需要在其主窗口区域之外绘制的应用程序。我已经必须编写代码才能实际进行绘图:

[DllImport("User32.dll")]
public static extern IntPtr GetDC(IntPtr hwnd);
[DllImport("User32.dll")]
public static extern void ReleaseDC(IntPtr hwnd, IntPtr dc);

IntPtr desktopPtr = GetDC(IntPtr.Zero);
Graphics g = Graphics.FromHdc(desktopPtr);

g.DrawLine(Pens.White, 0, 0, Screen.FromControl(this).WorkingArea.Width, Screen.FromControl(this).WorkingArea.Height);

g.Dispose();
ReleaseDC(IntPtr.Zero, desktopPtr);

但是,on paint 事件不适合放置代码,因为它不会在重绘窗体之外的内容时调用。所以我的问题是,这段代码可以放在哪里,以便在重绘屏幕的一部分时调用它?

最佳答案

如果您想要在屏幕上绘制内容,您应该始终创建一个窗口来保存该内容。在桌面(不属于您的窗口)上绘画不是个好主意。

解决方案是创建一个具有扩展样式 WS_EX_NOACTIVATE 的窗口,并在其上绘制以响应 WM_PAINT 消息。对于 WinForms 应用程序,运行时会在您获得 WM_PAINT 时调用 Form.OnPaint,以便您可以处理该事件并在那里进行绘制。演示:

[DllImport("User32.dll")]
private static extern IntPtr GetWindowLong(IntPtr hWnd, int index);
[DllImport("User32.dll")]
private static extern int SetWindowLong(IntPtr hWnd, int index, IntPtr value);
[DllImport("User32.dll")]
private static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);

static readonly IntPtr HWND_BOTTOM = new IntPtr(1);

private const int WS_EX_NOACTIVATE = 0x08000000;
private const int GWL_EXSTYLE = -20;

private const uint SWP_NOMOVE = 0x0002;
private const uint SWP_NOSIZE = 0x0001;
private const uint SWP_NOZORDER = 0x0004;
private const uint SWP_FRAMECHANGED = 0x0020;
private const uint StyleUpdateFlags = SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED;

public Form1()
{
    InitializeComponent();
    this.FormBorderStyle = FormBorderStyle.None;
    this.Paint += Form1_Paint;
    this.Shown += Form1_Shown;
}

private void Form1_Shown(object sender, EventArgs e)
{
    IntPtr currentStyle = GetWindowLong(this.Handle, GWL_EXSTYLE);
    int current = currentStyle.ToInt32();
    current |= WS_EX_NOACTIVATE;
    SetWindowLong(this.Handle, GWL_EXSTYLE, new IntPtr(current));
    SetWindowPos(this.Handle, IntPtr.Zero, 0, 0, 0, 0, StyleUpdateFlags);
}

private void Form1_Paint(object sender, PaintEventArgs e)
{
    e.Graphics.Clear(Color.Black);
}

如果您希望窗口 float 在顶部,请将表单的 TopMost 属性设置为 true。如果您希望您的窗口固定在 Z 顺序的底部(与 TopMost 完全相反),请将以下逻辑添加到您的表单中:

private struct WINDOWPOS
{
    public IntPtr hwnd;
    public IntPtr hwndInsertAfter;
    public int x;
    public int y;
    public int cx;
    public int cy;
    public uint flags;
}
private const int WM_WINDOWPOSCHANGING = 0x0046;
protected override void WndProc(ref Message m)
{
    if (m.Msg == WM_WINDOWPOSCHANGING)
    {
        if (m.LParam != IntPtr.Zero)
        {
            WINDOWPOS posInfo = Marshal.PtrToStructure<WINDOWPOS>(m.LParam);
            posInfo.hwndInsertAfter = HWND_BOTTOM;
            Marshal.StructureToPtr(posInfo, m.LParam, true);
            m.Result = IntPtr.Zero;
            return;
        }
    }
    base.WndProc(ref m);
}

这会处理 WM_WINDOWPOSCHANGING 窗口消息,并通过告诉窗口管理器将窗口放在底部来防止窗口在 Z 顺序中向上移动。

关于c# - 在窗体区域外绘图时如何重绘,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38034189/

相关文章:

node.js - Windows的 Electron 自动更新

c# - windiff 样式垂直线

c# - 当 FormBorderStyle 为 None 时,如何以编程方式显示窗口的系统菜单?

c# - c# 编译器如何处理此 DateTime.ToOADate()?

c# - 如何在 C# 中使用非常大的字典?

c# - 检索当前的 Outlook 约会

c++ - 操作指南 : programmatic install on windows?

c++ - 如何在调用 SelectItem 函数时强制 CTreeCtrl 不滚动到项目?

c# - 关于在 C# 中运行时关闭窗体

c# - 让 C# 识别 Visual Studio 之外的 dll