【问题标题】:How do you scale the title bar on a DPI aware win application?如何在 DPI 感知 win 应用程序上缩放标题栏?
【发布时间】:2015-10-25 05:37:00
【问题描述】:

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

这一切都很好,我可以在我的代码中很好地扩展客户区域中的任何内容。但是,我唯一的问题是,如果我将我的应用程序从系统 dpi 监视器拖到非系统 dpi 监视器,标题栏和任何系统菜单都会变得太大或太小。对于大多数内置应用程序(例如 calc、edge browser 等)来说,情况并非如此,因此必须有足够的空间来正确缩放它。有人知道 MS 的开发人员是如何做到这一点的吗?

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

Sample app 我正在附加一个非常简单的应用程序,它可以识别每个显示器的 dpi。

【问题讨论】:

  • 您的每个监视器代码是什么样的? WM_DPICHANGED 的处理程序?
  • 您的应用是通用应用吗?计算器和 Edge 都是
  • @melak47 这不是一个通用应用程序。这是一个旧应用程序 - 只是想让它在我所有的显示器上都能正常缩放。如果它是一个通用应用程序,你认为 windows 会缩放标题栏吗?
  • 嗯..好的。我不知道你怎么能称之为题外话,但这是我正在使用的off-the-shelf scaffold with the dpi-awareness code。设置 dpi 感知的代码在 wWinMain 中(不在清单中)。 WndProc 也处理 WM_DPICHANGED 但不做任何事情。您可以使用进程资源管理器验证应用程序的 dpi 感知(应用程序也运行自己的验证)
  • 好的。 afaik,示例应用程序非常标准,但我希望人们会发现它是相关的。

标签: c++ winapi windows-8.1 dpi windows-10


【解决方案1】:

有人知道 MS 的开发人员是如何做到这一点的吗?

这有一个非常令人失望的答案。使用Alin Constantin 的 WinCheat 并检查 Calculator 的顶级窗口,我看到窗口大小为 320x576,客户端大小也是 320x576。

换句话说,微软通过抑制窗口的非客户区完全避免了这个问题,而是把所有东西都放在客户区。使这项工作对您来说很好可能涉及标题栏的自定义绘制。

值得注意的是计算器和例如Windows 资源管理器不为标题栏使用相同的颜色。计算器做标题栏的自定义绘图可以完美地解释这一点。

【讨论】:

  • 谢谢。这正是我担心的。我实际上不明白这背后的原因。如果他们在 win10 中以正确的方式解决了这个问题,每个人都会从中受益。
  • @hvd "cmd.exe" 不知道,但会生成 "conhost.exe",它在 procexp 中显示为 per-monitor 感知。它可以完美缩放(在 100% 和 200% DPI 上)。运行对话框也可以完美缩放,但从中运行无效程序并显示未缩放的消息框。按下查找按钮,它会弹出一个完美缩放的打开文件对话框。哇。我意识到 cmd 和 run-dialog 可能是非常特殊的窗口,但是通过在客户区域中自己呈现所有内容来解释这种行为并不容易。
  • @jabe 是对的——conhost 是我见过的唯一一款真正做到每显示器 DPI 缩放 100% 正确且具有常规非客户区的应用程序。甚至控制台选项对话框也可以正确缩放。
  • 试图决定是否应该奖励这个问题或自己写一个新问题。 hvd,我喜欢你的回答,我认为它是正确的,但它还远远不够。正如在其他 cmets 中所指出的(特别是上面的我的),沼泽标准的 Win32 运行对话框正确地做到了这一点。据我所知,使用 Spy++,它 不是 所有者绘制其非客户区,我非常怀疑 shell 团队已将 Run 启动的 Browse 对话框子类化为所有者绘制 它的标题栏。所以必须有一些合理的方法来做到这一点。他们不可能完全是白痴。
  • @CodyGray 我希望自己看到一个更好的答案,我认为如果你悬赏这个问题,那么不仅有合理的机会不会得到这个问题,而且系统也会在何时将赏金奖励给我赏金的重点是得到比我的答案更好的东西。我认为创建一个新问题是合理的。包含指向此问题的链接,并在您的问题中说明,对于您的问题,您特别希望避免使用所有者绘制的标题栏,我认为不应将其错误地关闭为重复项。
【解决方案2】:

documentation 说:

请注意,每个显示器的 DPI 感知应用程序的非客户区不会被 Windows 缩放,并且在高 DPI 显示器上会按比例缩小。

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

【讨论】:

    【解决方案3】:

    更新:

    添加新的<dpiAwarness> 声明来清单就足以解决所有这些混乱。例如here

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

    关于该主题的更多调查。

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

    测试窗口移至辅助 96 dpi 显示器:

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

    注意标题栏过大和窗口图标大小错误。

    这是使用<dpiAware>true</dpiAware>的正确渲染

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

    叹息。

    【讨论】:

    • 如前所述,在周年更新(和较新版本的 Windows 10)上,您可以通过从 WM_NCCREATE 调用 EnableNonClientDpiScaling 将非客户区调整为 DPI 缩放。查看此示例:github.com/Microsoft/Windows-classic-samples/tree/master/…
    • 在 Windows 10 创意者更新中将有一个新的 DPI 感知上下文 DPI_AWARENESS_PER_MONITOR_AWARE_V2,它将默认为顶级窗口启用非客户端缩放。
    【解决方案4】:

    Windows 10 周年更新 (v1607) 添加了一个新 API,您必须调用它才能启用非客户端区域的 DPI 缩放:EnableNonClientDpiScaling。当收到WM_NCCREATE 时,应该调用此函数。消息在窗口创建期间被发送到窗口的过程回调。

    例子:

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

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

    来自the documentation

    默认情况下不启用顶级窗口的非客户端缩放。您必须调用此 API 以为您希望自动为其非客户区缩放的每个单独的顶级窗口启用它。一旦这样做,就无法禁用它。启用非客户端缩放意味着系统为窗口绘制的所有区域都会自动缩放以响应窗口上的 DPI 变化。这包括标题栏、滚动条和菜单栏等区域。当您希望操作系统负责根据监视器的 API 以正确的大小自动渲染这些区域时,您需要调用 EnableNonClientDpiScaling

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

    【讨论】:

    猜你喜欢
    • 2011-05-03
    • 1970-01-01
    • 2016-02-04
    • 1970-01-01
    • 1970-01-01
    • 2021-09-27
    • 1970-01-01
    • 2012-12-01
    相关资源
    最近更新 更多