c# - 带有 DWM : how to handle WM_NCCALCSIZE correctly 的自定义窗框

标签 c# winforms-interop dwm

我正在尝试使用 DWM 为我的表单制作自定义窗口框架。 该平台是 C# WinForms,Pinvoking DWM。

MSDN article on making custom window frame with DWM 之后, 主要步骤如下:

  1. 移除标准框架(非客户区),返回 0 作为对 WM_NCCALCSIZE 消息的应答
  2. 使用 DwmExtendFrameIntoClientArea 函数将框架扩展到客户区

我用下一种方式处理 WM_NCCALCSIZE 消息:

protected override void WndProc(ref Message m)
{
   switch (m.Msg)
   {
       case WM_NCCALCSIZE:
            if (isDwmWindowFramePaintEnabled() && m.WParam != IntPtr.Zero)
            {
                m.Result = IntPtr.Zero;
            }
            else
            {
                base.WndProc(ref m);
            }
            return;
   }
}

根据 MSDN documentation on WM_NCCALCSIZE ,

When wParam is TRUE, simply returning 0 without processing the NCCALCSIZE_PARAMS rectangles will cause the client area to resize to the size of the window, including the window frame. This will remove the window frame and caption items from your window, leaving only the client area displayed.

除一个问题外,一切都很好,对我有用。 当我最大化/恢复窗口时,它总是在恢复时增长一点点。 我认为,问题是这样的:

  1. 当窗口恢复时,它只包含客户区
  2. Windows 试图给窗口一些非客户区
  3. 在 WM_NCCALCSIZE 客户区增长到包含非客户区

所以,每次我最大化/恢复它时,这个窗口都会变大一点。 我需要删除非客户区以使用 DWM 绘制自定义表单框架。 我不能简单地将窗口边框样式设置为无,因为 DWM 将不会绘制窗口标题和边框。

请帮助解决问题并愉快地拥有自定义窗框。

最佳答案

这实际上是 Windows 窗体中的一个错误,并且有一个解决方法。在函数 Form.SizeFromClientSize(int, int) 中,AdjustWindowRectEx 函数用于转换大小,它始终使用默认测量值,不能被覆盖。这个函数从两个地方调用:

  1. RestoreWindowBoundsIfNecessary 在 WM_WINDOWPOSCHANGED 窗口消息处理程序中
  2. SetClientSizeCore

解决方法如下:

  • 重写表单中的 CreateParams:

    private bool createParamsHack;
    
    protected override CreateParams CreateParams
    {
        get
        {
            CreateParams cp = base.CreateParams;
            // Remove styles that affect the border size
            if (createParamsHack)
                cp.Style &= ~(int)(WS_BORDER | WS_CAPTION | WS_DLGFRAME | WS_THICKFRAME);
            return cp;
        }
    }
    
  • 覆盖 WndProc 并插入以下代码来处理 WM_WINDOWPOSCHANGED:

        if (m.Msg == WM_WINDOWPOSCHANGED)
        {
            createParamsHack = true;
            base.WndProc(ref m);
            createParamsHack = false;
        }
    
  • 覆盖 SetClientSizeCore:

    protected override void SetClientSizeCore(int x, int y)
    {
        createParamsHack = true;
        base.SetClientSizeCore(x, y);
        createParamsHack = false;
    }
    

覆盖 SizeFromClientSize(Size) 以返回正确的测量结果也是个好主意,但这并不是绝对必要的。

关于c# - 带有 DWM : how to handle WM_NCCALCSIZE correctly 的自定义窗框,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20085317/

相关文章:

wpf - 开口WPF用户控件的winform窗口缩小父的winform窗口

c# - 没有 Aero Glass 的 DwmExtendFrameIntoClientArea

c++ - All-aero 窗口混合了控件的颜色——如何避免这种情况

c# - 多个字段的 MVC 表单验证

c# WPF 维护加载程序的单个实例

c# - ObjectListView 显示图标

c# - 表单编辑器自动将子控件添加到 WPF Integration ElementHost

wpf - WPF 用户控件中的键盘输入不会发送到 WinForms 容器

c# - 使用 C#、WPF 和 DWM 保存窗口的屏幕截图

c# - 通用属性比较 - 识别属性变化