5

In a Winforms app, I followed the general guidance in how-to-write-winforms-code-that-auto-scales-to-system-font-and-dpi-settings and in .Net Framework high-dpi support to enable PerMonitorV2 DPI Scaling. I am using .NET Framework 4.8 on a system post Windows 10 Anniversary Update (1607) to make use of the latest high DPI support features.

The DPI scaling looks great if the app is started on the primary monitor or any monitor with the same scaling as the primary monitor but the scaling is completely wrong if any Form (the main Form of the app or a secondary top level Form) is first shown on a display with different DPI than the primary monitor. For example, if the app is started on a 4k/250% scaling laptop screen (with four other monitors at 1920x1080/100% scaling) then the Form gets displayed at 1:1 scaling on the 4k screen and shows up as a tiny 1 square inch on that screen (with only the Title and MenuBar correctly scaled): Image Showing Bad Scaling.

The issue appears to be caused by the fact that the CurrentAutoScaleDimensions of the Form are not being set correctly in these cases. They appear to be set to the current "dimensions" of the primary screen and not the screen that the Form is being shown on. However, if the Form is first shown on the primary screen and then moved to a screen with different DPI, the CurrentAutoScaleDimensions do get correctly updated to reflect the actual DPI of the destination screen and the Form gets scaled correctly. So, for example, if I set the primary screen to be the 4k/250% screen and then start the app on a 1920x1080/100% screen, the CurrentAutoScaleDimensions (incorrectly) get set to those for the 4k/250% screen and result in the Form being extremely overscaled. But if I start the app on the primary 4k/250% screen, it gets scaled correctly when first shown and then also scales correctly as it is dragged to other monitors (and back). In summary, when a Form is first shown, the CurrentAutoScaleDimensions seem to be always getting set to the primary monitor dimensions and not the dimensions of the screen that the Form is being shown on.

Does anyone know of remedy for this situation?

JohnC
  • 81
  • 1
  • 5
  • 1
    https://github.com/dotnet/winforms/issues/4854 – Hans Passant Jan 04 '22 at 00:11
  • Thank you @HansPassant! Very enlightening and much appreciated. I am not yet a Git expert but if I read that link correctly, the issue I described above is a known bug that has been corrected but the fix was not included in .NET 6.01. I will use my workaround solution below until this fix is released and then try transitioning to .NET. Thanks much! – JohnC Jan 04 '22 at 14:38

2 Answers2

3

The best solution I have been able to come up with is to force the application to always start up on the Primary Screen regardless of what screen it was launched from. This seems to fix all issues with initial scaling of the main Form and all subsequent scaling as it is dragged between monitors seems fine:


public Form1()
    {
    this.Font = SystemFonts.IconTitleFont;

    // Force the main Form of the app to always open on the primary screen
    // to get scaling to work.

    this.Location = Screen.PrimaryScreen.WorkingArea.Location;
    this.StartPosition = FormStartPosition.Manual;
    InitializeComponent();
    ...

For secondary Forms generated by the application, I have found that they get scaled correctly if I first show them on the primary screen, then hide and redisplay them on the screen containing the application's main Form (where I want them to be displayed). In some cases, I found it was sufficient to just call f.PerformLayout() rather than actually showing the form on the primary screen, but that did not work in all cases.


private void secondFormToolStripMenuItem_Click(object sender, EventArgs e)
    {
    FormExtra f = new FormExtra(this);

    // First display the Form on the primary screen to make scaling correct

    f.StartPosition = FormStartPosition.Manual;
    f.Location = Screen.PrimaryScreen.WorkingArea.Location;
    f.Show();

    // Then hide it and move it where we really want it

    f.Hide();
    f.StartPosition = FormStartPosition.CenterParent;
    f.ShowDialog();
    }

This solution is not ideal in that the form briefly flashes on another screen which seems rather kludgy. However, this solution does seem to result in correct scaling of all Forms in the application regardless of what monitor the app is started on. I have tested it on two different multi-monitor configurations so far.

JohnC
  • 81
  • 1
  • 5
  • My primary objective in posting this is to share this information in case it is of any use to others since I could not find much information on this issue and struggled for quite some time to even come up with this kludgy solution. Hopefully, someone comes along with a nicer solution though! – JohnC Jan 03 '22 at 23:42
  • 2
    Nice self-answered question. Thanks for documenting this. Plus-ones all around – Flydog57 Jan 03 '22 at 23:48
  • If my primary screen (and my only screen) is 125% scaling by default (it's my laptop HiDPI screen), how do I make my WinForms program run at 100% scaling and keep the font sizes the same amount of pixels as if they were run on a monitor set to 100% scaling. – Felix An Mar 13 '23 at 03:27
0

I have the same issue. My primary screen is 4K scaled to 175% in Windows. My secondary screen is 2560x1440 scaled at 125%. When opening a form in the same position as a user has previously dragged it to, if that is on the secondary screen it is too big. As soon as the user moves the form, even by a tiny amount, the screen resizes to be correct for the scaling.

What I have done is to add:

Me.Left = Me.Left + 1 (vb.net but I suspect that C# is similar) to the forms Load Sub

Private Sub frmSelectNewIx_Load(ByVal sender As Object, ByVal e As EventArgs) Handles MyBase.Load
            Me.Left = Me.Left + 1
            AddHandler LocationChanged, AddressOf SaveNewFormPosition ' In a module to save the forms new position using a timer.

......

This forces the screen to move and the autoscaling kicks in without an obvious visual glitch.