Skip to content

Fix incorrect ClientSize calculation in PerMonitorV2 mode when form moves between monitors with different DPI#14277

Open
LeafShi1 wants to merge 1 commit intodotnet:mainfrom
LeafShi1:Fix_12132_PerMonitorV2_autoscale_lead_to_wrong_ClientSize
Open

Fix incorrect ClientSize calculation in PerMonitorV2 mode when form moves between monitors with different DPI#14277
LeafShi1 wants to merge 1 commit intodotnet:mainfrom
LeafShi1:Fix_12132_PerMonitorV2_autoscale_lead_to_wrong_ClientSize

Conversation

@LeafShi1
Copy link
Member

@LeafShi1 LeafShi1 commented Feb 6, 2026

Fixes #12132

Root Cause

The original code in OnGetDpiScaledSize applied the font-based autoScaleFactor to the entire window Size (including non-client area):

desiredSize.Width = (int)(Size.Width * autoScaleFactor.Width);
desiredSize.Height = (int)(Size.Height * autoScaleFactor.Height);

However, the non-client area (title bar, borders) is scaled linearly by DPI ratio by Windows, not by the font-based auto scale factor. This mismatch caused incorrect ClientSize calculations when forms transitioned between monitors with different DPI settings.

Proposed changes

When calculating the desired window size during DPI transitions in OnGetDpiScaledSize, the non-client area (title bar and window borders) should be excluded from the autoScaleFactor calculation since Windows scales them linearly by DPI ratio, not by the font-based auto scale factor.

The fix:

  1. Uses AdjustWindowRectExForDpi to calculate the non-client area size at both old and new DPI
  2. Subtracts the old DPI's non-client area from the current Size to get the client area
  3. Scales only the client area by autoScaleFactor
  4. Adds the new DPI's non-client area to get the final desired size

Customer Impact

  • After the fix, the ClientSize difference is reduced to 0-1 pixels (the remaining 1 pixel is an inherent rounding error from font measurement in AutoScaleMode.Font).

Regression?

  • No

Risk

  • minimal

Screenshots

Before

image

After

image

Test methodology

  • Manually

Test environment(s)

  • .net 11.0.0-preview.2.26080.101
Microsoft Reviewers: Open in CodeFlow

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes a long-standing bug in PerMonitorV2 DPI scaling where forms moving between monitors with different DPI settings would have incorrect ClientSize values. The issue was that the original code applied the font-based autoScaleFactor to the entire window size (including non-client area), but Windows actually scales the non-client area (title bar, borders) linearly by DPI ratio, not by the font-based factor.

Changes:

  • Modified OnGetDpiScaledSize method to separately calculate non-client area sizes at both old and new DPI values
  • Changed calculation to apply autoScaleFactor only to the client area, not the entire window
  • Added Math.Round to minimize rounding errors during DPI transitions

Comment on lines +4614 to +4616
}

// Calculate client area at old DPI
Copy link

Copilot AI Feb 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

According to the C# coding guidelines for this codebase, there should be an empty line after structure blocks (like if/else) for clarity. An empty line should be inserted between the closing brace of the else block and the comment that follows.

Copilot generated this review using guidance from repository custom instructions.
Comment on lines 4625 to 4627
Debug.WriteLine($"AutoScaleFactor computed for new DPI = {autoScaleFactor.Width} - {autoScaleFactor.Height}");

// Notify Windows that the top-level window size should be based on AutoScaleMode value.
Copy link

Copilot AI Feb 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

According to the C# coding guidelines for this codebase, there should be an empty line before return statements for clarity. An empty line should be inserted between line 4625 (Debug.WriteLine) and line 4627 (the comment before return).

Copilot generated this review using guidance from repository custom instructions.
@codecov
Copy link

codecov bot commented Feb 6, 2026

Codecov Report

❌ Patch coverage is 72.22222% with 5 lines in your changes missing coverage. Please review.
✅ Project coverage is 77.17894%. Comparing base (9b546b7) to head (29f9636).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files
@@                 Coverage Diff                 @@
##                main      #14277         +/-   ##
===================================================
- Coverage   77.18395%   77.17894%   -0.00501%     
===================================================
  Files           3279        3279                 
  Lines         645138      645154         +16     
  Branches       47730       47731          +1     
===================================================
- Hits          497943      497923         -20     
- Misses        143503      143539         +36     
  Partials        3692        3692                 
Flag Coverage Δ
Debug 77.17894% <72.22222%> (-0.00501%) ⬇️
integration 18.99167% <72.22222%> (+0.00103%) ⬆️
production 52.05334% <72.22222%> (-0.00986%) ⬇️
test 97.40479% <ø> (ø)
unit 49.48632% <0.00000%> (-0.01631%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Multi-Monitor] PerMonitorV2 autoscale lead to wrong ClientSize.

2 participants