Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WinForms HighDPI PerMonitorV2 windows launched on secondary monitor is incorrectly scaled #4854

Closed
Balkoth opened this issue Apr 29, 2021 · 7 comments · Fixed by #5557
Closed
Assignees
Labels
area-HDPI-SA Issues related to high DPI SystemAware mode

Comments

@Balkoth
Copy link
Contributor

Balkoth commented Apr 29, 2021

.NET Core Version:

SDK: 5.0.202 (db7cc87d51)
Runtime: 5.0.5 (2f740adc14)

Problem description:

If the main application window is not launched on the main monitor it scales wrong, because Form.DeviceDpi has the DPI-Settings of the main monitor.

Minimal repro:

  • Create new .NET-Core 5.0 WinForms app
  • Set Application.SetHighDpiMode(HighDpiMode.PerMonitorV2)
  • Open the forms designer for Form1
  • Set AutoScaleMode = Dpi
  • Set StartPosition = CenterScreen;

-> Build and run the project by launching it not on your primary monitor, but on a secondary monitor with different DPI from the primary monitor. You can do this for example by launching debugging from Visual Studio and quickly focusing a window on another monitor so that this monitor gets the focus.

I have attached a sample project with the repro steps described above here:
TestDpiChanging.zip

@RussKie RussKie changed the title WinForms HighDPI PerMonitorV2 windows not launching on main monitor is incorrectly scaled WinForms HighDPI PerMonitorV2 windows launched on secondary monitor is incorrectly scaled Apr 29, 2021
@RussKie RussKie added the area-HDPI-SA Issues related to high DPI SystemAware mode label Apr 29, 2021
@RussKie RussKie added this to the 6.0 milestone Apr 29, 2021
@Balkoth
Copy link
Contributor Author

Balkoth commented Apr 29, 2021

Here is a workaround until this is fixed:

internal const uint WM_DPICHANGED = 0x02E0;

[DllImport("user32.dll")]
internal static extern int GetDpiForWindow(IntPtr hwnd);

[DllImport("user32.dll")]
internal static extern IntPtr SendMessage(IntPtr hWnd, uint msg, IntPtr wParam, ref RECT lParam);

internal static IntPtr MakeLParam(int lowWord, int highWord)
{
  return (IntPtr)((highWord << 16) | (lowWord & 0xffff));
}

[StructLayout(LayoutKind.Sequential)]
internal struct RECT
{
  public int left;
  public int top;
  public int right;
  public int bottom;
}

protected override void OnHandleCreated(EventArgs e)
{
  base.OnHandleCreated(e);

  int realDpi = GetDpiForWindow(this.Handle);
  double scaleFactor = (double)realDpi / (double)this.DeviceDpi;

  int newWidth = (int)(this.Size.Width * scaleFactor);
  int newHeight = (int)(this.Size.Height * scaleFactor);

  int dpiLeft = this.Location.X + ((this.Size.Width - newWidth) / 2);
  int dpiTop = this.Location.Y + ((this.Size.Height - newHeight) / 2);;
  int dpiRight = dpiLeft + newWidth;
  int dpiBottom = dpiTop + newHeight;

  RECT rect = new RECT() { left = dpiLeft, top = dpiTop, right = dpiRight, bottom = dpiBottom };
  SendMessage(this.Handle, WM_DPICHANGED, MakeLParam(realDpi, realDpi), ref rect);
}

@dreddy-work
Copy link
Member

dreddy-work commented Jun 8, 2021

You can do this for example by launching debugging from Visual Studio and quickly focusing a window on another monitor so that this monitor gets the focus.

@Balkoth , i think you mentioned same scenario in a different issue. Can you please elaborate on how "quickly focusing a window on another monitor so that this monitor gets the focus" is helping you launching application on secondary screen?

@Balkoth
Copy link
Contributor Author

Balkoth commented Jun 9, 2021

I don't know how to say this in simpler words, but there is another method. Just put the windows explorer on the monitor in question and launch the exe via doubleclick from there.

@dreddy-work
Copy link
Member

dreddy-work commented Jun 9, 2021

No worries. Sample app attached helped understand that you were explicitly setting position on Form to CenterScreen. Will be looking into it.

dreddy-work added a commit that referenced this issue Aug 26, 2021
…th different DPI settings.

In this case, Form Handle is created according to the Secondary monitor DPI hence, doesn't receive `DPI_CHANGED` message.
Fix is making sure we scale the Form according to DPI when Handle is created.

Fixes #4854 and related issues.
@ghost ghost added the 🚧 work in progress Work that is current in progress label Aug 26, 2021
@dreddy-work dreddy-work modified the milestones: 6.0, 7.0 alpha1 Aug 27, 2021
@dreddy-work dreddy-work removed the 🚧 work in progress Work that is current in progress label Aug 30, 2021
dreddy-work added a commit that referenced this issue Sep 7, 2021
…th different DPI settings. (#5557)

* Fixing scaling issue with initializing Form on non-primary monitor with different DPI settings.

In this case, Form Handle is created according to the Secondary monitor DPI hence, doesn't receive `DPI_CHANGED` message.
Fix is making sure we scale the Form according to DPI when Handle is created.

Fixes #4854 and related issues.
@ghost ghost removed this from the 7.0 alpha1 milestone Sep 7, 2021
@Ashley-Li
Copy link

Verified this issue with .NET SDK 7.0.100-alpha.1.21479.26, it is fixed that WinForms HighDPI PerMonitorV2 windows launched on secondary monitor is correctly scaled.
930
.

filipnavara pushed a commit to emclient/winforms that referenced this issue Nov 11, 2021
…th different DPI settings. (dotnet#5557)

* Fixing scaling issue with initializing Form on non-primary monitor with different DPI settings.

In this case, Form Handle is created according to the Secondary monitor DPI hence, doesn't receive `DPI_CHANGED` message.
Fix is making sure we scale the Form according to DPI when Handle is created.

Fixes dotnet#4854 and related issues.
@Olina-Zhang
Copy link
Member

@dreddy-work verify this issue in HDPI full testing again on .Net 7.0 build, I found this issue was not fixed well on both .Net 7.0 and .Net 6.0 builds: the Form deviceDpi value is not correct on secondary screen. Please see following testing result:
.Net 7.0 launch test app on 300% secondary screen directly:
image
.Net 6.0 launch test app on 300% secondary screen directly:
image

@Olina-Zhang
Copy link
Member

Form is scaled well when launching app in secondary screen directly, we can check it in Spy++, but the control: label gets Form.DeviceDpi value is not correct when launching app in secondary screen directly.
Here is the result when launch app on 300% DPI secondary screen directly:
image

filipnavara pushed a commit to emclient/winforms that referenced this issue Dec 27, 2021
…th different DPI settings. (dotnet#5557)

* Fixing scaling issue with initializing Form on non-primary monitor with different DPI settings.

In this case, Form Handle is created according to the Secondary monitor DPI hence, doesn't receive `DPI_CHANGED` message.
Fix is making sure we scale the Form according to DPI when Handle is created.

Fixes dotnet#4854 and related issues.
@ghost ghost locked as resolved and limited conversation to collaborators Jan 28, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-HDPI-SA Issues related to high DPI SystemAware mode
Projects
None yet
5 participants