Channels ▼
RSS

.NET

Coding for High-DPI Displays in Windows


Finally, it is necessary to change the code that is executed with the WM_PAINT message in the WndProc function with the following code that calls the RenderWindow function:

case WM_PAINT:
	RenderWindow(hWnd);
	break;

The result of all this code is simple — a window that scales its size and its fonts to display crisp and clear text for the different DPI levels (see Figure 1).

WindowsDisplay
Figure 1: The sample application window scaled to 192 DPI and displaying crisp text.

Creating a Per-Monitor DPI Aware Win32 Application

If you move the window to a monitor with a different DPI setting, DWM will scale up or down the application and you will see blurry text on the new display. If I want this simple application to adapt itself to a different display or to dynamic changes in the DPI settings, it is necessary to change its manifest file to convert it to a per-monitor DPI aware application and make a few small changes in the code to listen for changes in the DPI settings or scale factor. The use of a scale helper class will make it easy to perform the conversion.

The following lines add the code that has to be executed with the WM_DPICHANGED message in the WndProc function. This message tells the program that most of its window is on a monitor with a new DPI setting. The wParam contains the new DPI setting and the lParam contains a window rectangle scaled for the new DPI. It is necessary to ask the scale helper to recalculate the scale based on the new DPI setting, resize the window, create scaled fonts, and render again the window content.

case WM_DPICHANGED:
	// wParam = New DPI setting
	// lParam = Window rectangle scaled for the new DPI
	// Use the new DPI retrieved from the wParam to calculate the new scale factor
	g_ScaleHelper.SetScaleFactor(LOWORD(wParam));
	// Get the window rectangle scaled for the new DPI, retrieved from the lParam
LPRECT lprcNewScale;
	lprcNewScale = (LPRECT)lParam;
	// Resize the window for the new DPI setting
	SetWindowPos(hWnd, HWND_TOP, lprcNewScale->left, lprcNewScale->top, RectWidth(*lprcNewScale), RectHeight(*lprcNewScale), SWP_NOZORDER | SWP_NOACTIVATE);
	// Create new scaled fonts for the new DPI setting
	CreateFonts(hWnd);
	// Render the window contents again
	RedrawWindow(hWnd, NULL, NULL, RDW_ERASE | RDW_INVALIDATE);
	break;

Right-click on the project name (DPIAware) within Solution Explorer and select Properties. Visual Studio will show the Property Pages dialog box for the project. Then, select Configuration Properties | Manifest Tool | Input and Output | DPI Awareness, select Per Monitor High DPI Aware from the dropdown menu, and click OK. Execute the application, and perform one of the following actions:

  • Change the DPI settings for the display in which the application is showing the window.
  • Move the application window to another display with a different DPI setting than the display in which the application started.

You will see how the application changes its window, button, and text sizes and keeps displaying crisp text, taking full advantage of the new DPI setting (see Figure 2).

WindowsDisplay
Figure 2: The sample application window scaled to 240 DPI and displaying crisp text.

However, there is a small problem with the value generated for the manifest file. Now, the application is per-monitor DPI-aware, but in Windows versions prior to 8.1, that value isn't recognized. Thus, Windows Vista, Windows 7, and Windows 8 will consider the application as a DPI-unaware application and the DWM will scale it. Unless you only target Windows 8.1, you won't want this to happen.

You can solve this problem creating an XML manifest file with a special value for dpiAware: True/PM. This value sets the application to per-monitor DPI-aware on Windows 8.1, but sets the application to system DPI-aware on Windows Vista through Windows 8. This way, your application will behave as a system DPI-aware one on Windows versions lower than 8.1. The following lines show the XML code for the manifest you have to create:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1"
  manifestVersion="1.0">
  <application xmlns="urn:schemas-microsoft-com:asm.v3">
    <windowsSettings>
      <dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">True/PM</dpiAware>
    </windowsSettings>
  </application>
</assembly>

You need to save the XML manifest and tell Visual Studio to merge it. Right-click on the project name (DPIAware) within Solution Explorer and select Properties. Visual Studio will show the Property Pages dialog box for the project. Then, select Configuration Properties | Manifest Tool | Input and Output | DPI Awareness, and select None. You don't want Visual Studio to specify and DPI awareness configuration in the manifest because you provide it with the manifest file. Then, specify the full path for the XML manifest file you saved in Additional Manifest Files and click OK. Now, the application will work as a per-monitor DPI aware application in Windows 8.1, and as a system DPI aware application in previous Windows versions.

As you can see from this simple example, if you code a scale helper class, it is pretty easy for you to convert a system DPI-aware application into a per-monitor DPI aware one. You can use either the retrieved DPI value or the calculated scale factor to load the necessary resources that have been prepared for the different scale factors you want to support. End users will definitely welcome your investment in providing crisp and clear text and images for their different high-DPI displays.


Gastón Hillar is a senior contributing editor at Dr. Dobb's.

Related Article

Programming for High-Res Displays in Windows


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.
 

Video