Tuesday, November 25, 2014

Making DPI-Aware Applications in .NET

Since the 1980s, the Microsoft Windows operating system has used a default dots per inch (DPI) setting of 96 pixels per inch (PPI). Until recently this setting was hardly ever changed. A developer could pretty safely assuming any computer running their software would be using a DPI setting of 96 PPI. However, high definition displays are becoming more common. Laptop computers have been using higher DPI displays for a while now (e.g. Apple’s MacBook Pro with Retina display uses a 220 or 227 PPI display, Acer Aspire S7 uses a 220 PPI display, Google Chromebook Pixel uses a 239 PPI display). In October 2014 Apple released their 5120 x 2880 pixel resolution (218 PPI) monitor. Dell has announced it will begin selling an UltraSharp 27 Ultra HD 5K in December 2014 with a 5128 x 2880 pixel resolution (218 PPI). Software developers can no longer assume the computer running their software will be using 96 PPI.

WinForms Applications

By default WinForms applications are not DPI-aware, they assume a 96 PPI. If they are executed on a system with a higher DPI setting, the operating system will virtualize and scale these applications to account for the higher DPI setting. This can cause the applications to suffer from many visual artifacts, including incorrect scaling of UI elements, clipped text, and blurry images. The following are screenshots of a WinForms application running at 96 PPI and 144 PPI:

WinForms Not DPI-Aware - 96 PPI
Figure 1 - WinForms Application 96 PPI

WinForms Not DPI-Aware - 144 PPI
Figure 2 - WinForms Application 144 PPI

The application running at 144 PPI appears blurry, unlike the version running on a 96 PPI display.

Making WinForms Applications DPI-Aware

A WinForms application can be made DPI-aware by adding the following <dpiAware> element to the application’s manifest file:

<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
   <asmv3:application>
      <asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
         <dpiAware>true</dpiAware>
      </asmv3:windowsSettings>
   </asmv3:application>
</assembly>

Adding this content to the application’s manifest file resolves the blurry text and controls issues seen in the non-DPI-aware WinForms application.

WinForms DPI-Aware - 144 PPI
Figure 3 - WinForms DPI Aware Application 144 PPI

When developing a WinForms application that will be run on machines with various DPI settings, it is wise to test the application with the standard DPI settings (100% (96 PPI); 125% (120 PPI); 150% (144 PPI), and 200% (192 PPI)).

WPF Applications

By default Windows Presentation Foundation (WPF) applications are system DPI-aware. WPF uses device-independent units to facilitate resolution and device independence. WPF automatically scales each device-independent unit based on current system DPI. This results in applications that look correct regardless of the machine’s DPI setting.

WPF DPI-Aware - 96 PPI
Figure 4 - WPF Application 96 PPI

WPF DPI-Aware - 144 PPI
Figure 5 - WPF Application 144 PPI

Per-Monitor DPI-Aware WPF Application

In Windows 8.1 Microsoft added the ability to have different DPI settings for each monitor, instead of a single system DPI setting. WPF applications are system DPI-aware, but they are not monitor DPI-aware. Microsoft has published a detailed article describing how to create a monitor DPI aware application on their website, called “Developing a Per-Monitor DPI-Aware WPF Application” (http://msdn.microsoft.com/en-us/library/windows/desktop/ee308410.aspx). This is useful for applications that will be used on systems with multiple monitors that have different DPI settings.

Conclusion

High definition displays are here to stay. As application developers we need to ensure our applications look and work properly on traditional 96 PPI systems as well as the new higher PPI system. The newer WPF UI framework handles differing DPI settings by default, but even WinForms applications can be made to work properly on higher PPI systems.

The source code that was used to generate the screenshots for this article can be downloaded at https://drive.google.com/file/d/0ByqdgeUw6Qs3UVAtLXR4Ukc3WG8/view?usp=sharing.

No comments: