【问题标题】:Problems with per-monitor DPI aware MFC MDI app每个监视器 DPI 感知 MFC MDI 应用程序的问题
【发布时间】:2021-07-22 00:53:51
【问题描述】:

我正在努力使 MFC 应用程序在具有不同 DPI 缩放比例的多个监视器环境中正确显示。当主显示器和辅助显示器以不同的 DPI 运行并且应用位于辅助显示器上时,我无法解释一个问题。

如果主显示器具有 100% DPI 缩放 (96) 而辅助显示器具有相同的 100% DPI 缩放,则一切正常。

如果主监视器具有 100% DPI 缩放 (96) 而辅助监视器具有 125% 缩放 (120 DPI) 或 150% 缩放 (144 DPI) 或任何其他更高的值,当子窗口最大化时,部分子窗口系统栏可见,如下所示:

125% 缩放:

150% 缩放:

如果您仔细观察,125% 的 7 个像素和 150% 的 14 个像素。假设系统栏在 100% 缩放时为 29 个像素,在 150% 时为 36 和 125%,以及 43 个像素,这 7 和 14 个像素分别是 125% 和 150% 时栏大小与 100% 时的高度差基线。

因此,条形图的位置和大小似乎是由系统在主监视器上运行时计算的。

当你最大化子窗口时,会有一系列Windows消息发送到窗口:WM_GETMINMAXINFO > WM_WINDOWPOSCHANGING > WM_GETMINMAXINFO > WM_NCCALSIZE > WM_WINDOWSPOSCHANGED > WM_MOVE > @987654333 @。 WM_GETMINMAXINFO 当窗口的大小或位置即将改变时发送,以便应用程序可以覆盖,例如,窗口的默认最大化大小和位置。对此有一个注释:

对于具有多个监视器的系统,ptMaxSize 和 ptMaxPosition 成员描述窗口的最大化大小和位置 主监视器,即使窗口最终最大化到 辅助监视器。在这种情况下,窗口管理器会调整这些 值来补偿主监视器和 显示窗口的监视器。因此,如果用户离开 ptMaxSize untouched,显示器上比主窗口大的窗口 监视器最大化到较大监视器的大小。

Raymond Chan 有一篇文章对此进行了解释:How does the window manager adjust ptMaxSize and ptMaxPosition for multiple monitors?

所以ptMaxSize 应该填充主显示器的尺寸。我的主显示器是 2560x1440 像素,辅助显示器的尺寸是 1920x1200。但是,我在这里得到的尺寸值是 1757x1023 和 1761x1027(在连续调用时)。这既不是主监视器的大小,也不是辅助监视器的大小。

我试图做一个肮脏的把戏并处理WM_NCCALCSIZE 消息并将位置(左,上)设置为0(相对于父级)。

void CMyMDIChildWnd::OnNcCalcSize(BOOL bCalcValidRects, NCCALCSIZE_PARAMS* lpncsp)
{
   CMDIChildWnd::OnNcCalcSize(bCalcValidRects, lpncsp);   
   if (condition)
   {
      lpncsp->rgrc[0].left = 0;
      lpncsp->rgrc[0].top = 0;
   }
}

只要子窗口有焦点就可以正常工作。如果我单击另一个窗口并且它失去焦点,则该栏将重新绘制并显示在先前的位置。这个技巧只是说客户区从哪里开始,所以当非客户区被重绘时,我会回到原来的问题。

我的问题是这个问题的根源是什么,我该如何解决?

【问题讨论】:

  • 编写 MFC 应用程序的方法有很多种。无论有或没有每显示器高 DPI 感知设置和两个不同的显示器,我都不会重现标准 MFC 模板的任何问题。但是你的带有 2 个图标的蓝色“丝带”看起来并不标准。你有复制代码/项目吗?
  • 不,我没有可重现的代码。我制作了一个 MDI MFC 应用程序,最大化子窗口效果很好。我的应用程序的 UI 是完全定制的。但这并不是那么特别。蓝色条是菜单栏,它下面是工具栏。我没有截取整个窗口,只截取了相关部分。
  • 很抱歉我无法构建项目来重现您的问题,所以我仍然希望您能提供一个可重现的示例。也许你可以参考this可能相关的线程。
  • 我猜你对 主窗体的非客户区处理有一些问题。
  • 实际上,主框架不处理WM_NCPAINT消息和其他非客户端消息(WM_PAINTWM_ERASEBKGND都不处理)。那里没什么大不了的。

标签: c++ winapi visual-c++ mfc dpi-aware


【解决方案1】:

我可能遇到了同样的现象,尽管来自不同的路线,我的解决方案与你的非常相似,只是有点额外,也许这对你有帮助?

void CFixedFrame::OnNcCalcSize ( BOOL bCalcValidRects, NCCALCSIZE_PARAMS* lpncsp )
{
    MDIBASEWND::OnNcCalcSize ( bCalcValidRects, lpncsp ) ;

    if ( bCalcValidRects )
    {
        RECT& rcNew = lpncsp->rgrc[0];
        RECT& rcOld = lpncsp->rgrc[1];
        RECT& rcClient = lpncsp->rgrc[2];

        // My problem arose because of the Aero bug (hardwired border widths)
        // And also problems with Theming
        const CNonClientMetrics ncm;
        rcNew.top = ncm.iCaptionHeight + Aero related stuff not relevant to your problem
    }
}

CNonClientMetrics 在哪里...

class CNonClientMetrics : public NONCLIENTMETRICS
{
public:
    CNonClientMetrics ( )
    {
        cbSize = sizeof ( NONCLIENTMETRICS ) - sizeof ( this->iPaddedBorderWidth ) ;
        SystemParametersInfo ( SPI_GETNONCLIENTMETRICS, sizeof ( NONCLIENTMETRICS ), this, 0 ) ;
    }
} ;

[以 DPI 感知方式] 这让我摆脱了烦人的蓝色边框(带有按钮)。据我了解,您不能再禁用绘制该部分的 DWM。

我现在找不到我原来的参考资料,但在我对这个问题的注释中,它只发生在 MFC 的 MDI 框架窗口。

不过,this link 也可能有用吗?

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-05-03
    • 1970-01-01
    • 2017-06-14
    • 2012-12-01
    • 1970-01-01
    • 1970-01-01
    • 2016-03-13
    相关资源
    最近更新 更多