Search code examples
c#winformsfontsdpidpi-aware

How to configure an app to run correctly on a machine with a high DPI setting (e.g. 150%)?


I've created a simple Winforms application in C#. When I run the application on a machine with high DPI settings (e.g. 150%), the application gets scaled up. So far so good! But instead of rendering the fonts with a higher font size, all texts are just scaled up, too. That of course leads to very blurry text (on all controls like buttons etc.).

Shouldn't windows take care of rendering the texts correctly? For example my application's title bar is rendered crisp & clear.


Solution

  • Once you go past 100% (or 125% with the "XP-style DPI scaling" checkbox ticked), Windows by default takes over the scaling of your UI. It does so by having your app render its output to a bitmap and drawing that bitmap to the screen. The rescaling of that bitmap makes the text inevitably look fuzzy. A feature called "DPI virtualization", it keeps old programs usable on high resolution monitors.

    You have to explicitly let it know that you can handle higher DPI settings by adding the <dpiAware> element to your manifest. The MSDN page is here but it isn't complete since it is omitting the UAC settings. Project + Add New Item, pick "Application Manifest File". Edit the manifest text or copy/paste this:

    <?xml version="1.0" encoding="utf-8"?>
    <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3" >
        <assemblyIdentity version="1.0.0.0" name="MyApplication.app"/>
        <trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
            <security>
                <requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
                    <requestedExecutionLevel level="asInvoker" uiAccess="false" />
                </requestedPrivileges>
            </security>
        </trustInfo>
        <asmv3:application>
            <asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
                <dpiAware>true</dpiAware>
            </asmv3:windowsSettings>
        </asmv3:application>
    </assembly>
    

    You can also pinvoke SetProcessDPIAware() in your Main() method, necessary for example if you deploy with ClickOnce:

        [STAThread]
        static void Main() {
            if (Environment.OSVersion.Version.Major >= 6) SetProcessDPIAware();
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new Form1());             // Edit as needed
        }
    
        [System.Runtime.InteropServices.DllImport("user32.dll")]
        private static extern bool SetProcessDPIAware();
    

    UPDATE, this common need is finally a bit easier if you use VS2015 Update 1 or higher. The added manifest already has the relevant directive, just remove the comments.


    Keyword for search so I can find this post back: dpiAware