如何缩放 DPI 感知 win 应用程序的标题栏?

How do you scale the title bar on a DPI aware win application?

我通过在清单文件中设置 <dpiAware>True/PM</dpiAware> 使每个监视器的应用程序 dpi-aware。我可以使用进程资源管理器验证这确实有效或通过调用 GetProcessDpiAwareness。

一切正常,我可以在我的代码中很好地缩放客户区中的任何内容。但是,我唯一的问题是,如果我将我的应用程序从 system-dpi 显示器拖到 non-system dpi 显示器,标题栏和任何系统菜单都会变得太大或太小。对于大多数 built-in 应用程序(例如 calc、edge 浏览器等)来说情况并非如此,因此必须远离才能正确缩放它。有人知道 MS 的开发人员是如何做到这一点的吗?

下面的截图应该能更好地解释我的问题。另请注意,关闭、最小和最大按钮之间的填充在缩放 (96dpi) 时是不同的。

Sample app 我正在附加一个非常简单的应用程序,它支持 per-monitor dpi。

Does anyone how the devs at MS did this?

这是一个非常令人失望的答案。使用 Alin Constantin 的 WinCheat 并检查计算器的 top-level window,我看到 window 大小为 320x576,客户端大小也是 320x576。

换句话说,微软通过抑制window的non-client区域完全避免了这个问题,而是将所有内容都放在客户区。使这项工作对您有用可能涉及标题栏的自定义绘图。

值得注意的是计算器和例如Windows Explorer 不对标题栏使用相同的颜色。自定义绘制标题栏的计算器可以完美地解释这一点。

documentation 说:

Note that the non-client area of a per monitor–DPI aware application is not scaled by Windows, and will appear proportionately smaller on a high DPI display.

您 link 通过删除非客户区并使客户区覆盖整个 window 来处理此问题的 Microsoft 应用程序。

更新:

只需添加新的 <dpiAwarness> 声明即可解决所有这些问题。例如 here.

以前调查的残余(过时):

对该主题进行更多调查。

系统设置:两台显示器,一台为 96 dpi,另一台为 267 dpi (Microsoft Surface 4)。

测试 window 已移至辅助 96 dpi 显示器:

这是清单中 <dpiAware>true/pm</dpiAware> 的渲染(错误,IMO):

请注意标题栏的巨大尺寸和 window 图标的错误尺寸。

这里是使用 <dpiAware>true</dpiAware>

的正确渲染

而且我怀疑 MSDN 文档明显误导了 PROCESS_DPI_AWARENESS 的值。我没有看到 <dpiAware>true</dpiAware><dpiAware>true/pm</dpiAware> 之间的消息和样式有任何差异。后一个只会使标题变大。在这两种情况下,应用程序在具有不同 DPI 的显示器之间移动时都会收到 WM_DPICHANGED 消息。

感叹

Windows 10 周年更新 (v1607) 添加了一个新的 API 必须调用才能启用非客户区的 DPI 缩放:EnableNonClientDpiScaling. This function should be called, when WM_NCCREATE 已收到。在 window 创建期间,消息被发送到 window 的过程回调。

示例:

case WM_NCCREATE:
{
    if (!EnableNonClientDpiScaling(hWnd))
    {
        // Error handling
        return FALSE;
    }

    return DefWindowProcW(...);
}

如果应用程序的 DPI 感知上下文是 DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2,则应省略调用 EnableNonClientDpiScaling,因为它不会有任何效果,尽管该函数仍会 return 成功.

来自the documentation

Non-client scaling for top-level windows is not enabled by default. You must call this API to enable it for each individual top-level window for which you wish to have the non-client area scale automatically. Once you do, there is no way to disable it. Enabling non-client scaling means that all the areas drawn by the system for the window will automatically scale in response to DPI changes on the window. That includes areas like the caption bar, the scrollbars, and the menu bar. You want to call EnableNonClientDpiScaling when you want the operating system to be responsible for rendering these areas automatically at the correct size based on the API of the monitor.

有关 Windows 10 AU 中 DPI 缩放更改的更多信息,请参阅 this blog post