C# 使用DWM自定义窗口框架:如何正确处理WM\u NCCALCSIZE

C# 使用DWM自定义窗口框架:如何正确处理WM\u NCCALCSIZE,c#,winforms-interop,dwm,C#,Winforms Interop,Dwm,我正在尝试使用DWM为我的表单创建自定义窗口框架。 平台是C#WinForms,Pinvoking DWM 以下是主要步骤: 删除标准帧(非客户端区域),在回答WM_NCCALCSIZE消息时返回0 使用DwmExtendFrameIntoClientArea函数将框架扩展到客户端区域 我用下一种方式处理WM_NCCALCSIZE消息: protected override void WndProc(ref Message m) { switch (m.Msg) { c

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

以下是主要步骤:

  • 删除标准帧(非客户端区域),在回答WM_NCCALCSIZE消息时返回0
  • 使用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;
       }
    }
    
    根据,

    当wParam为TRUE时,只返回0而不处理 NCCALCSIZE_参数矩形将导致客户端区域调整为 窗口的大小,包括窗框。这将删除 从窗口中删除窗口框架和标题项,只保留 显示客户端区域

    除了一个问题,一切都很好,对我来说都很好。 当我最大化/还原窗口时,当它被还原时,它总是增长一点。 我认为,问题是这样的:

  • 恢复窗口时,它只包含客户端区域
  • Windows尝试为窗口提供一些非客户端区域
  • 在WM_NCCALCSIZE中,客户端区域增长为包含非客户端区域
  • 所以,每次我最大化/恢复它时,这个窗口都会增长一点。 我需要删除非客户端区域以使用DWM绘制自定义表单框架。 我不能简单地将窗口边框样式设置为“无”,因为DWM将不会绘制窗口标题和边框


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

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

  • 在WM_WINDOWPOSCHANGED窗口消息处理程序中必要时恢复Windows边界
  • 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)
    以返回正确的测量值也可能是个好主意,但这并不是绝对必要的。

    谢谢!我很快就会试试看,看有没有用!很抱歉,这个答复耽搁了。我尝试了你的建议,覆盖了
    CreateParams
    ,效果很好!另外,我尝试重写方法“Form.SizeFromClientSize(Size)”,只返回客户端大小,该大小作为参数传递给该方法。但那没用。