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



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

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

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

根据WM_NCCALCSIZE上的MSDN文档,

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

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

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

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

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

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

  1. WM_WINDOWPOSCHANGED窗口消息处理程序中的RestoreWindowBoundsIfNecessary
  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_WINDOWOSCHANGED:

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

相关内容

  • 没有找到相关文章

最新更新