Fix HDPI: Position of a Modal-Form (and Modeless) is not centered to parent#14664
Fix HDPI: Position of a Modal-Form (and Modeless) is not centered to parent#14664Sathish-087 wants to merge 4 commits into
Conversation
Add DPI scaling check for forms starting at center parent.
|
@Sathish-087 please read the following Contributor License Agreement(CLA). If you agree with the CLA, please reply with the following information.
Contributor License AgreementContribution License AgreementThis Contribution License Agreement ( “Agreement” ) is agreed to by the party signing below ( “You” ), 1. Definitions. “Code” means the computer software code, whether in human-readable or machine-executable form, “Project” means any of the projects owned or managed by .NET Foundation and offered under a license “Submit” is the act of uploading, submitting, transmitting, or distributing code or other content to any “Submission” means the Code and any other copyrightable material Submitted by You, including any 2. Your Submission. You must agree to the terms of this Agreement before making a Submission to any 3. Originality of Work. You represent that each of Your Submissions is entirely Your 4. Your Employer. References to “employer” in this Agreement include Your employer or anyone else 5. Licenses. a. Copyright License. You grant .NET Foundation, and those who receive the Submission directly b. Patent License. You grant .NET Foundation, and those who receive the Submission directly or c. Other Rights Reserved. Each party reserves all rights not expressly granted in this Agreement. 6. Representations and Warranties. You represent that You are legally entitled to grant the above 7. Notice to .NET Foundation. You agree to notify .NET Foundation in writing of any facts or 8. Information about Submissions. You agree that contributions to Projects and information about 9. Governing Law/Jurisdiction. This Agreement is governed by the laws of the State of Washington, and 10. Entire Agreement/Assignment. This Agreement is the entire agreement between the parties, and .NET Foundation dedicates this Contribution License Agreement to the public domain according to the Creative Commons CC0 1. |
There was a problem hiding this comment.
Pull request overview
This PR updates WinForms Form positioning logic to make StartPosition = CenterParent behave consistently for both modal (ShowDialog) and modeless (Show(owner)) forms, especially when autoscaling and DPI changes occur before the form becomes visible.
Changes:
- Invoke
AdjustFormPosition()duringOnLoadwhenStartPositionisCenterParent, so modeless owned forms get centered after autoscaling. - Invoke
AdjustFormPosition()duringWM_DPICHANGEDhandling for hiddenCenterParentforms to correct centering across DPI transitions. - Add unit tests validating that both
ShowDialog(owner)andShow(owner)withCenterParentare centered on the parent.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
src/System.Windows.Forms/System/Windows/Forms/Form.cs |
Centers CenterParent forms during OnLoad and re-centers hidden CenterParent forms after DPI changes. |
src/test/unit/System.Windows.Forms/System/Windows/Forms/FormTests.cs |
Adds unit tests asserting centering behavior for modal and modeless owned forms using CenterParent. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| // Also, at this time we can now locate the form on the correct area of the screen. | ||
| // We must do this after applying any autoscaling. | ||
| if (GetState(States.Modal)) | ||
| if (GetState(States.Modal) || (FormStartPosition)_formState[s_formStateStartPos] == FormStartPosition.CenterParent) |
There was a problem hiding this comment.
In a cross-DPI scenario (e.g., parent is on a hi-DPI monitor), the sequence could be:
CreateHandle()— window created on primary monitorWM_DPICHANGEDfires (!Visible && CenterParent) →AdjustFormPosition()OnLoadfires (CenterParent) →AdjustFormPosition()again
This results in AdjustFormPosition() being called twice. It’s harmless (idempotent), but worth noting. Consider adding a comment here explaining why the double call is acceptable, or adding a guard to skip if already adjusted.
| // Also, at this time we can now locate the form on the correct area of the screen. | ||
| // We must do this after applying any autoscaling. | ||
| if (GetState(States.Modal)) | ||
| if (GetState(States.Modal) || (FormStartPosition)_formState[s_formStateStartPos] == FormStartPosition.CenterParent) |
There was a problem hiding this comment.
Previously only modal forms (ShowDialog) reached this path, and ShowDialog always has an owner. Now non-modal Show() can also reach here. What happens if the user calls:
childForm.StartPosition = FormStartPosition.CenterParent;
childForm.Show(); // no ownerDoes AdjustFormPosition() handle Owner == null gracefully? Could you verify this edge case doesn’t throw or produce unexpected positioning?
| OnDpiChanged(e); | ||
| if (!Visible && (FormStartPosition)_formState[s_formStateStartPos] == FormStartPosition.CenterParent) | ||
| { | ||
| AdjustFormPosition(); |
There was a problem hiding this comment.
The !Visible guard is correct — it prevents overriding the user’s position after the form is shown. However, the intent isn’t immediately obvious to future readers. Consider adding a comment, e.g.:
// Only re-center before the form is shown to the user. Once visible,
// the user may have repositioned it and we must not override that.| }; | ||
|
|
||
| childForm.ShowDialog(parentForm); | ||
| // Expected center: parentForm center minus half of childForm size. |
There was a problem hiding this comment.
childForm.Width and childForm.Height are read after ShowDialog returns (i.e., after Close() was called inside the Load handler). If Close() triggers a WM_DPICHANGED (e.g., the form moves to a different monitor during teardown), the size could change.
Consider capturing the size inside the Load handler alongside the location:
Size capturedSize = Size.Empty;
childForm.Load += (sender, e) =>
{
capturedLocation = childForm.Location;
capturedSize = childForm.Size;
childForm.Close();
};Then use capturedSize for the expected position calculation.
Fixes #10422
Proposed changes
Customer Impact
Regression?
Risk
Screenshots
Before
CenterParentIssueDemo.mp4
After
CenterParentFixDemo.mp4
Test methodology
Accessibility testing
Test environment(s)
Microsoft Reviewers: Open in CodeFlow