Trade-Ideas Philip
Trade-Ideas Philip

Reputation: 1247

Identical fonts don't look identical on high DPI monitors

I've got a WinForms application that works great on older systems, but I'm having trouble making it look good on 4k monitors. There are multiple issues, and a lot written on the subject, but this question is focused on one specific problem. I can set different controls to use the same font, but on high DPI systems, the controls will look a lot different. How can I fix this?

Obviously I can change the font size, move controls around, etc. But Windows is adding a mysterious factor into my font sizes. Without knowing what Windows is doing, it's hard for me to undo it!

On an older system my test window looks perfect:

All controls have the same font size.

On a high DPI system, some controls have a different font size than others:

Requested font size does not match visible font size.

I've tried several things, including manually setting the font on some controls rather than inheriting from the form. As you can see, changing the font did not fix the problem:

enter image description here

After searching the Internet I've tried several things to fix this including:

I only found one thing that fixed my problem. Unfortunately I had to do it on the target machine, not on the machine where I'm building this. So it's not a practical solution. See the second item under "steps to repeat" for more details.

Steps to repeat:

Upvotes: 4

Views: 718

Answers (1)

Trade-Ideas Philip
Trade-Ideas Philip

Reputation: 1247

This is crazy but it works.

Everything I've seen on the internet about DPI Virtualization says that Windows will automatically set a process to PROCESS_DPI_UNAWARE by default. So unless you explicitly pick one of the other two settings, your application should look decent on a high resolution monitor. It might be a little fuzzy, but it shouldn't look as bad as the examples I've shown above.

Apparently that's not true. The default depends on the computer, and it depends on the day. My solution: Explicitly set the application to use PROCESS_DPI_UNAWARE. I've included a code sample below.

Note that you should be able to take care of this using the manifest. Some sources say that's the preferred way, rather than using C# code. We've had mixed results with that. The C# code option seems more reliable.

    [DllImport("shcore.dll")]
    static extern int SetProcessDpiAwareness(_Process_DPI_Awareness value);

    enum _Process_DPI_Awareness
    {
        Process_DPI_Unaware = 0,
        Process_System_DPI_Aware = 1,
        Process_Per_Monitor_DPI_Aware = 2
    }


    public MainForm()
    {

        //int result = SetProcessDpiAwareness(_Process_DPI_Awareness.Process_System_DPI_Aware);
        //int result = SetProcessDpiAwareness(_Process_DPI_Awareness.Process_Per_Monitor_DPI_Aware);
        int result = SetProcessDpiAwareness(_Process_DPI_Awareness.Process_DPI_Unaware);
        System.Diagnostics.Debug.Assert(result == 0);

This works on a number of different developer machines. We're about to start sending the fix out to beta testers.

Summary

  1. The O/S provides a compatibility mode for old programs running on high DPI systems.
  2. WinForms and the O/S provide tools for manually changing the sizes of your controls depending on the DPI of the system or the current monitor.
  3. Both #1 and #2 are both seriously buggy!
  4. The details var a lot from one computer to the next.
  5. Fixing #2 would be the more powerful option, but as far as I can tell it would be impossible to fix that.
  6. Instead I fixed #1. That works reasonably well.

Upvotes: 2

Related Questions