TX Text Control .NET for Windows Forms

High DPI and Display Scaling

Topic

Laptops and 4K monitors run at scale factors of 150% or 200%, and many users move windows between screens with different ones. Whether TX Text Control looks sharp on such displays is decided by one setting of your application, its DPI awareness, which Windows Forms leaves off unless you switch it on. What TX Text Control does at each level of awareness, how to set it in .NET and .NET Framework projects, and what your own ribbon buttons need are covered below.

When an application already looks blurry or keeps the wrong size, High DPI and Scaling in Troubleshooting starts from the symptom.

How TX Text Control Scales

Windows gives every application one of these kinds of DPI awareness:

AwarenessWhat Windows doesWhat TX Text Control does
Not DPI awareDraws the window at 96 DPI and stretches the finished image to the scale factor, which makes it blurryDraws at 96 DPI
System DPI awareTells the application the system DPI when it startsSince version 27.0, draws the document, the ribbons, the dialog boxes, the rulers and the selection frames for this DPI, with high-resolution images, on displays of 300 DPI and more
Per-monitor DPI awareTells the application when its window moves to a display with another DPI, or the scale factor changesSince version 29.0, the TextControl and all its toolbars and ribbons change their scale while the application runs, on Windows 10 version 1703 and later

For a new application, choose per-monitor awareness: the editor stays sharp on every display. The TX Words samples of TX Text Control are configured this way.

The images of the built-in ribbon tabs, toolbars and dialog boxes are based on vector graphics, which TX Text Control renders for the current DPI. The blog article New Icon Set Preview: Making your Applications DPI Aware introduced this icon set, and shows the icons at a large size:

Icons of the TX Text Control icon set, such as chart types, tables, databases, scissors, a magnifier, a QR code and a folder, drawn from vector graphics at a large size without blurred edges.

Make a .NET Application DPI Aware

In a project for .NET 8 or higher, set the DPI awareness in Program.cs, before the first form is created. The following Main method is the one of the TX Words sample:

using System;
using System.Windows.Forms;

static class Program
{
    [STAThread]
    static void Main()
    {
        Application.EnableVisualStyles();
        Application.SetHighDpiMode(HighDpiMode.PerMonitorV2);
        Application.SetCompatibleTextRenderingDefault(false);
        Application.Run(new Form1());
    }
}

Application.SetHighDpiMode and the enumeration HighDpiMode belong to Windows Forms. PerMonitorV2 makes the application per-monitor DPI aware; SystemAware would make it system DPI aware. Call it before the first form is created, as the sample does. For .NET, Microsoft's article High DPI support in Windows Forms also points to project-level application settings as another place for the same value.

Make a .NET Framework Application DPI Aware

In the .NET Framework 4.7 and later, per-monitor DPI awareness of Windows Forms is switched on in the application configuration, as Microsoft's article High DPI support in Windows Forms describes:

  1. Add an application manifest to the project, if it has none, with Project > Add New Item... > Application Manifest File. Declare compatibility with Windows 10 in its compatibility element:

    <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
      <application>
        <!-- Windows 10 compatibility -->
        <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
      </application>
    </compatibility>
    
  2. Add the DpiAwareness key to App.config, inside the configuration element:

    <System.Windows.Forms.ApplicationConfigurationSection>
      <add key="DpiAwareness" value="PerMonitorV2" />
    </System.Windows.Forms.ApplicationConfigurationSection>
    
  3. Keep Application.EnableVisualStyles() as the first call in Main, and rebuild.

The .NET Framework version of TX Words uses the same App.config entry. Microsoft no longer recommends setting the DPI awareness in the manifest itself, because the manifest overrides App.config.

Give Your Own Ribbon Buttons Sharp Icons

TX Text Control rescales the images of its own ribbon items. A button your code adds keeps the image your code assigned, so your code has to give it an image for the current DPI, and a new one when the DPI changes. ResourceProvider returns the built-in icons of TX Text Control for any DPI, identified by the name of an item. The following code in Form1.cs follows the TX Words sample: it reads the DPI when the window is created and updates the icons of a print button named printButton when Windows reports a new DPI:

using System.Drawing;
using System.Windows.Forms;
using TXTextControl.Windows.Forms;

public partial class Form1 : Form
{
    private const int WM_CREATE = 0x0001;
    private const int WM_DPICHANGED = 0x02E0;
    private float dpi;

    protected override void WndProc(ref Message message)
    {
        switch (message.Msg)
        {
            case WM_CREATE:
                // The resolution of the display the window is created on
                using (Graphics g = Graphics.FromHwnd(Handle))
                {
                    dpi = g.DpiX;
                }
                break;

            case WM_DPICHANGED:
                // The new resolution is in the low word of WParam
                int newDpi = (int)message.WParam & 0xFFFF;
                if (newDpi != dpi)
                {
                    dpi = newDpi;
                    SetPrintButtonIcons();
                }
                break;
        }

        base.WndProc(ref message);
    }

    private void SetPrintButtonIcons()
    {
        // Built-in icons in the size the new resolution needs
        string print = ResourceProvider.FileMenuItem.TXITEM_Print.ToString();
        printButton.LargeIcon = ResourceProvider.GetLargeIcon(print, dpi);
        printButton.SmallIcon = ResourceProvider.GetSmallIcon(print, dpi);
    }
}

The code works as follows:

  • WndProc receives the Windows messages of the form. WM_CREATE arrives when the window is created, WM_DPICHANGED when it has moved to a display with another DPI or the scale factor has changed.
  • GetLargeIcon and GetSmallIcon return the icon of the item for the given DPI. ResourceProvider.FileMenuItem.TXITEM_Print identifies the print command; the RibbonItem values of the predefined tabs identify their items.
  • LargeIcon and SmallIcon of the RibbonButton show the new icons.
  • printButton is a RibbonButton your code created, as Custom Tabs and Commands shows. Call SetPrintButtonIcons once after creating it as well.

The TX Words Ribbon sample contains the complete code for all its buttons in MainWindow_DpiChanged.cs; see TX Words Sample Application.

Test on Several Displays

Change the scale factor in the Windows display settings while the application runs, and move its window between two displays with different scale factors. The document, the ribbon, the rulers and the status bar should stay sharp and keep their size relative to the text. An image that stays small or blurry belongs to a control your code draws; give it an image for the new DPI, as above.

Next Steps