当前位置: 首页 > 知识库问答 >
问题:

用DWM自定义窗框:如何正确处理WM_NCCALCSIZE

宋明亮
2023-03-14

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

按照MSDN关于用DWM制作定制窗框的文章,主要步骤如下:

  1. 移除标准帧(非客户端区域),回复WM_NCCALCSIZE消息返回0
  2. 使用DwmExtenFrameIntoClientArea函数将框架扩展到客户端区域

我以下一种方式处理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不会绘制窗口标题和边框。

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

共有1个答案

姬康平
2023-03-14

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

  1. 在WM_WINDOWPOSCHANGED窗口消息处理程序中必要时恢复Windows边界
  2. SetClientSizeCore

解决方法如下:

> < li>

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

 类似资料:
  • 我想对自定义JFX组件进行子类化,以更改/扩展它们的行为。作为一个真实的例子,我想扩展一个具有编辑功能的data viewer组件。 考虑下面非常小的场景。使用类非常有效。但是当实例化子类(在FXML文件中)时,不再注入字段 会自动调用该方法。 我知道将超级组件嵌套在子组件中应该可以很好地工作,但是我仍然想知道使用继承是否可以做到这一点。 将的可见性扩大到显然无法解决此问题。在中结合定义扩展点(此

  • 在 node.js中,通常的做法是将错误消息作为回调函数的第一个参数返回。在纯JS中有许多解决方案(promise,步骤,seq等),但它们似乎都无法与ICS集成。在不损失太多可读性的情况下处理错误的正确解决方案是什么? 例如:

  • 我想为我的应用程序使用自定义异常处理程序。但是,它不能正常工作。 ApplicationExceptionHandler.java authFilterRegistration.java ErrorEntity.java 使用这些代码,我希望有一个像这样的异常错误 这是的实例,但我得到了以下输出 从示例中可以清楚地看出,我无法完全覆盖默认的Java异常。仅更改成功的消息部分。我错过什么了吗?

  • EasySwoole支持用户自定义error handler 创建错误处理器 实现ErrorHandlerInterface接口 namespace App; use CoreAbstractInterfaceErrorHandlerInterface; class Test implements ErrorHandlerInterface { function handler( $msg

  • 问题内容: Java的I / O类,,,和他们的不同子类中都有一个可抛出的方法。 对于处理此类异常的正确方法是否存在共识? 我经常看到建议,只是默默地忽略它们,但这是错误的,至少在打开用于写的资源的情况下,关闭文件时出现问题可能意味着无法写入/发送未刷新的数据。 另一方面,在阅读资源时,我还不清楚为什么会抛出异常以及如何处理。 那么有什么标准建议吗? 问题答案: 记录下来。 您实际上不能 做任何事

  • 在我的REST API中,我有一个过滤器,该过滤器检查每个请求,以查看令牌是否是原样。下面是代码。 当用户登录到应用程序时,将调用上述代码。但是,令牌将在60分钟内过期。我知道,在令牌过期后,要么我必须带用户返回登录屏幕,要么刷新令牌。我把这里和这里的建议都看了一遍 但我不明白以下几点。 如何分配并将此令牌发送回用户?当前,当用户登录时,他将获得令牌并将其保存在一个变量中。为了使刷新的令牌工作,我