For .Net 8.0 WinForms apps go HERE (Also 7.0 and 6.0)
The below is for .Net Framework, latest being 4.8.
As posted in another post, many apps have problems with windows scaling.
See these screen-dumps from Windows lengthy guide that was found here
UPDATE 2020. After Windows “Creators update”, they made things slightly more complicated, sorry…
Here is a reasonably minimalistic approach. You will often just need code like this:
public partial class Form1 : Form {
public Form1() {
InitializeComponent();
DPI_Per_Monitor.TryEnableDPIAware(this, SetUserFonts);
}
void SetUserFonts(float scaleFactorX, float scaleFactorY) {
var OldFont = Font;
Font = new Font(OldFont.FontFamily, 11f * scaleFactorX, OldFont.Style, GraphicsUnit.Pixel);
Refresh();
OldFont.Dispose();
}
protected override void DefWndProc(ref Message m) {
DPI_Per_Monitor.Check_WM_DPICHANGED(SetUserFonts,m, this.Handle);
base.DefWndProc(ref m);
}
}
You will need to obey a few rules, but often it can be implemented in existing code in minutes!
UPDATE: You will now also have add (or modify) an app.manifest source-file for your project, with content similar to this
<?xml version="1.0" encoding="utf-8"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
<assemblyIdentity version="1.0.0.0" name="YourAppName"/>
<application xmlns="urn:schemas-microsoft-com:asm.v3">
<windowsSettings>
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness>
</windowsSettings>
</application>
</assembly>
For the whole thing to work we need a small helper class, source here (it is also updated 2020)
Here is the comment from the top of that:
DPI_Per_Monitor
Use to make a simple Windows form app truly DPI-aware.
That is AVOID woolly apps!
1) Make sure your forms are designed with font size set in PIXELS, not POINTS (yes!!)
All controls possible should use inherited fonts
Each font explicitly given in PIXEL needs to be handled in the callback function
provided to Check_WM_DPICHANGED
The standard “8.25pt” matches “11.0px” (8.25 *96/72 = 11)
2) Leave the form in the default Autoscale=Font mode
3) Insert call to DPI_Per_Monitor.TryEnableDPIAware() , right after InitializeComponent();
4) Add suitable call to DPI_Per_Monitor.Check_WM_DPICHANGED_WM_NCCREATE in DefWndProc
If a DefWndProc override is not already present add e.g. these lines
protected override void DefWndProc(ref Message m) {
DPI_Per_Monitor.Check_WM_DPICHANGED_WM_NCCREATE(SetUserFonts,m, this.Handle);
base.DefWndProc(ref m);
}
that has a call-back function you must provide, that set fonts as needed (in pixels or points),
if all are inherited, then just one set:
void SetUserFonts(float scaleFactorX, float scaleFactorY) {
var OldFont = Font;
Font = new Font(OldFont.FontFamily, 11f * scaleFactorX, OldFont.Style, GraphicsUnit.Pixel);
Refresh();
OldFont.Dispose();
}
5) And a really odd one, due to a Visual Studio BUG.
This ONLY works, if your PRIMARY monitor is scaled at 100% at COMPILE time!!!
It is NOT just a matter of using a different reference than the 96 dpi below, and
it does not help to run it from a secondary monitor set to 100% !!!
And to make things worse, Visual Studio is one of the programs that doesn’t handle
change of scale on primary monitor, without at the least a sign out….
NOTE that if you got (Checked)ListBoxes, repeated autosizing (e.g. move between monitors)
might fail as it rounds the height down to a multipla of the itemheight. So despite a
bottom-anchor it will ‘creep’ upwards…
So I recommend to place an empty and/or hidden bottom-anchored label just below the boxes,
to scale the spacing and set e.g. : yourList.Height=yourAnchor.Top-yourList.Top
Also note that not everything gets scaled automatically. Only new updates of Win10 handles
the titlebar correctly. Also the squares of checkboxes are forgotten.
UPDATE: a TINY improvement/change on the checkboxes after creators update. IF you start an app on a monitor not 100%, and stay there they ARE scaled correctly initially. But if you move the window to a monitor with a different DPI (or change DPI of current monitor), they still do not allow us to scale them correctly – but what can you expect with a product only a few years old and with only a few billion users…
But at the least it seems your primary monitor still does no longer has to be at 100% for VS to not mess up thing at compile time with VS2019 16.6.3, as was the case earlier (It messes with your source code changing positions…) Note that the new DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 mode is the only aware mode supported in VS now, no backwards compatibility after Win X “Creators update”!!
UPDATE: A few bugs have been ironed out here, thanks to Robin Krom in the comments
UPDATE: MS at the least admitted the scale-bug rather than fxing it:

Update 20231118. The VS2022 17.8 I just tried out, we still get this warning if the primary monitor is NOT at 100%. They still do not make new form apps scalable by default….

I came here more or less by accident, but noticed that you have a memory leak in your example.
Make sure you always dispose Fonts when they are no longer needed, so before assigning the new Font in SetUserFonts, dispose the previous!
Indeed, terribly sloppy, and did not include the style either…
(Fixed above and in the code using it in WindowsPosSaveNRestore )
Thanks for the update, it would be a shame if people copied the code and got bugs due to that.
Hi Eske, I followed all the instructions on here. However, whenever any code attempts to initialize a Form, I get the error message “Parameter is not valid”
This happens on monitors set to higher than 100%.
Uh, sad to hear. Must admit I have not experimented with it for a very long time, so they might have F.. it up in later update of either windows or VS.
Will try to find time to experiment, and see if it works here or not.
What is your monitor-config, what is the scale for primary/secondary?
As so much does not scale properly I have set my primary screen to emulate a coarser 1920×1200 resolution, and thus to work with 100% on all three (two external 48″ and 43″ 4K displays). Silly of course, but VS still does not work well if primary is not 100%, as I have whined about…. ADD: (Have not tried that for long either, so could be fixed)
I finally took some time to experiment…
I tried BOTH as “Framework 4.8” Windows Forms App and a “Net 8.0”, and the framework version worked, but the “Net 8.0” version required a different much simpler method, see this: https://eskerahn.dk/?p=5502
(I have both first and secondary monitor set to 100% and tried different scaling on the tertiary )
I found the bug!
For what ever reason you should make sure to NOT dispose the old font, before AFTER the refresh. Will fix above.