WinForms is the traditional .NET desktop application workload running on Windows. For historical reasons, there are some problems with its support for high DPI and for different DPI screens, and this article explores the possible solutions.

Windows "96" PPI history

The default DPI for Windows systems (more precisely called PPI) is 96. PPI means pixel per inch, which means how many pixels are displayed per inch screen.

The higher this value, the more delicate the display capability of the screen. But it also means that to display the same picture as a low PPI screen, a high PPI screen needs more pixels to fill. For a non-vector graph, this problem is not easy to solve, because of the high pixel stretch, the picture will be "pulled blurred." Similar problems occur in Windows, especially in older programs, which are designed with only 96 PPI in mind. This historical reason can be found in Microsoft's blog:

https://blogs.msdn.microsoft.com/fontblog/2005/11/08/where-does-96-dpi-come-from-in-windows/ 

Example: On a 150% DPI screen, the Windows Management Console (MMC) is blurred.

Windows 10 improvements

With more and more HDPI computers on the market today, Windows 10's semi-annual Feature Updates has been working to resolve DPI issues. We can fix many old programs with the settings shown in the following image. But it's hard to adapt across screens with different DPI.

The so-called Per Monitor-DPI aware means that when your computer has an external screen, Windows chooses a DPI that fits the screen to display an image of the external screen. This DPI is probably different from your computer's main screen. For example, with a 22-inch display with 1920x1080 attached to a Surface Pro, the Surface's home screen is typically more than 150% DPI, while the external display is 100%.

If the program doesn't support Per Monitor-DPI aware itself, you can try the compatibility mode that comes with Windows. You will find that although both screens have clear images, the app interface is magnified on a low DPI screen. It's not perfect. So the most authentic solution is to develop programs that support Per Monitor-DPI aware.

Microsoft's own applications have also improved regarding Per Monitor-DPI aware. For example, Visual Studio 2019 supports Per Monitor-DPI aware. (Windows 10 v1803 and .NET Framework 4.8)

What can we do about WinForms applications?

The three most native technologies for Windows desktop development are WinForms, WPF, and UWP. Because UWP was born in modern times, there is no DPI adaptation problem. WPF's XAML interface can also be easily adapted to DPI. But WinForms is way too old. Let's try to save it.

First of all, I use the 150% DPI main screen with VS2019, the designer view is not blurry, the control position is as follows:

Pay attention to the position of the red arrow. Everything's fine in VS. However, when it runs, the main screen at 150% DPI is blurry and the controls are misplaced.

Drag the window to the 100% DPI external screen, the UI is not blurry, but the controls are still misplaced.

According to Microsoft's documentation https://docs.microsoft.com/en-us/dotnet/framework/winforms/high-dpi-support-in-windows-forms, .NET Framework improves WinForms' DPI support starting at 4.7. So I changed the runtime of the program to 4.7.2 (Windows 10 1803 or more comes with it) Add an app.manifest file to the app root.

Uncomment Windows 10 GUID under assembly/compatibility/application.

<!-- Windows 10 -->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />

Add these into App.config

<System.Windows.Forms.ApplicationConfigurationSection>
  <add key="DpiAwareness" value="PerMonitorV2" />
</System.Windows.Forms.ApplicationConfigurationSection>

Now you find that the control position is displayed correctly on the home screen of 150% DPI, and the entire UI is not blurred.

But on a 100% DPI screen, although the UI is not blurry, the control position is still incorrect, and the TextBox control is huge.

There's no other way out of the Microsoft documentation. But I found that changing the runtime to .NET Framework 4.8 could fix the TextBox, with the control location still incorrect.

On closer inspection, it's not TextBox, Label, Checkbox that's causing the problem, but the MonthCalendar control is much wider on a 100% DPI screen than on a 150% DPI screen. And the combination of Panel, TableLayoutPanel and Dock won't solve the problem.

What about .NET Core 3.0

Net Core 3.0 is still in preview 6. From the results of my experiment, its DPI settings do not require App.config, but add this into program.cs:

Application.SetHighDpiMode(HighDpiMode.PerMonitorV2);

But unfortunately, the result is the same as above .NET Framework applications.

Conclusion

On Windows 10 v1903 (I haven't tried other versions), with .NET Framework 4.8, WinForms can get a certain level of Per Monitor-DPI aware, but some controls will be rendered in incorrect sizes, so you need to carefully test before publishing the application.