首页 > 解决方案 > 每个监视器 DPI 感知 MFC MDI 应用程序的问题

问题描述

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

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

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

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> WM_SIZEWM_GETMINMAXINFO当窗口的大小或位置即将改变时发送,以便应用程序可以覆盖,例如,窗口的默认最大化大小和位置。对此有一个注释:

对于具有多个监视器的系统,ptMaxSize 和 ptMaxPosition 成员描述了主监视器上窗口的最大化大小和位置,即使窗口最终在辅助监视器上最大化。在这种情况下,窗口管理器会调整这些值以补偿主监视器和显示窗口的监视器之间的差异。因此,如果用户保持 ptMaxSize 不变,则监视器上大于主监视器的窗口将最大化为较大监视器的大小。

Raymond Chan 有一篇文章,对此进行了解释:窗口管理器如何为多个显示器调整 ptMaxSize 和 ptMaxPosition?.

所以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;
   }
}

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

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

标签: c++winapivisual-c++mfcdpi-aware

解决方案


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

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 Frame Windows 上。

不过,这个链接也可能有用吗?


推荐阅读