2

I have been tasked with adding support for Per-Monitor V2 DPI awareness to a legacy WinForms application which targets .NET 4.7.2. I know this is a complicated issue and I've done a lot of reading on StackOverflow and the MSDN on this topic. Using a test project, I've got the basics working. The test application rescales as the DPI changes.

Here is my problem: The legacy software contains a lot of custom fonts on each Form (varied styles and sizes), and I'm having trouble getting those fonts to rescale when DPI changes during the runtime. It seems that I can set any font I like on the Form, and it will properly rescale if I use AutoScaleMode.Dpi on the Form. However once I set a custom font on a child Control, no rescaling is applied to that Control. The size remains unchanged as DPI changes. If DPI is unchanged, this is not a problem. The issue only occurs when moving between monitors with different DPI settings or changing the DPI settings of a monitor during the runtime. (Note: All font changes occur in the designer)

Here is an example:

Monitor 1 - 96 DPI

Monitor 1 - 96 DPI

Monitor 2 - 144 DPI

Monitor 2 - 144 DPI

You can see that checkBox3 is bold and scaled correctly at 96 DPI. However when I drag the application to Monitor 2, checkbox3 remains unscaled and so now it is smaller than the other text.

The loss of rescaling does sort of make sense to me because setting a custom font on a child Control breaks the ambient font relationship. Based on my tests, the Form actually gets a new font with the new DPI scaled point size when the DPI has changed. It makes sense that controls which aren't using that font aren't updated. But is there no way to request that all controls rescale themselves?

I have been trying to figure this out in the aforementioned test project (C# .NET 4.7.2). The only way I've found to make custom fonts rescale is to manually recreate the custom fonts when the DPI has changed by using the DPI change events.

private void CheckBox3_DpiChangedAfterParent(object sender, EventArgs e)
{
    checkBox3.Font = new Font(Font.FontFamily, 
                              checkBox3.Font.SizeInPoints * _dpiScaleFactor, 
                              checkBox3.Font.Style);
}

private void Form1_DpiChanged(object sender, DpiChangedEventArgs e)
{
   _dpiScaleFactor = (float)e.DeviceDpiNew / e.DeviceDpiOld;
}

Monitor 2 - 144 DPI (with manually font resizing)

Monitor 2 - 144 DPI (with manually font resizing)

So this does seem to work. Surely there is a better way though. What am I missing here?

Jason Tyler
  • 1,331
  • 13
  • 26

0 Answers0