使用DWM的自定义窗口框架:如何正确处理WM_NCCALCSIZE

Art*_*kyi 5 c# dwm winforms-interop

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

关于使用DWM制作自定义窗口框架MSDN文章之后,主要步骤如下:

  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;
   }
}
Run Code Online (Sandbox Code Playgroud)

根据有关WM_NCCALCSIZE的MSDN文档,

当wParam为TRUE时,只返回0而不处理NCCALCSIZE_PARAMS矩形将导致客户区调整大小到窗口大小,包括窗口框架.这将从窗口中删除窗口框架和标题项目,仅显示客户区域.

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

  1. 当窗口恢复时,它仅包含客户区
  2. Windows尝试向窗口提供一些非客户区域
  3. 在WM_NCCALCSIZE中,客户区域增长到包含非客户区域

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

请帮助解决问题,并愉快地拥有自定义窗口框架.

Fil*_*ara 6

这实际上是 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;
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)
  • 覆盖 WndProc 并插入以下代码来处理 WM_WINDOWPOSCHANGED:

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

    protected override void SetClientSizeCore(int x, int y)
    {
        createParamsHack = true;
        base.SetClientSizeCore(x, y);
        createParamsHack = false;
    }
    
    Run Code Online (Sandbox Code Playgroud)

重写SizeFromClientSize(Size)以返回正确的测量值也可能是个好主意,但这并不是绝对必要的。