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

时间:2013-11-20 00:50:43

标签: c# winforms-interop dwm

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

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

  1. 删除标准框架(非客户区域),回答0以回答WM_NCCALCSIZE消息
  2. 使用DwmExtendFrameIntoClientArea函数将框架扩展到客户区域
  3. 我以下一种方式处理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

      

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

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

    1. 当窗口恢复时,它仅包含客户区
    2. Windows尝试向窗口提供一些非客户区域
    3. 在WM_NCCALCSIZE中,客户区增长到包含非客户区
    4. 因此,每当我最大化/恢复它时,这个窗口就会增长一点点。 我需要删除非客户区域以使用DWM绘制自定义表单框架。 我不能简单地将窗口边框样式设置为无,因为DWM不会绘制窗口标题和边框。

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

1 个答案:

答案 0 :(得分:4)

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

    WM_WINDOWPOSCHANGED窗口消息处理程序中的
  1. RestoreWindowBoundsIfNecessary
  2. SetClientSizeCore
  3. 解决方法如下:

    • 在表单中覆盖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)以返回正确的测量值也可能是个好主意,但这并不是绝对必要的。