PerMonitorDPI/readme.md
Developer Guide
This developer guide provides developer info on how to test your WPF application with our Per Monitor DPI feature released in .NET 4.6.2 along with the Windows 10 Anniversary Update.
In order to enable Per Monitor DPI Awareness in your app, you should be running Windows 10 Anniversary Update or higher.
WPF apps are System DPI aware by default, and need to declare themselves to be Per Monitor DPI aware via an app.manifest file. To add an app.manifest:
<application xmlns="urn:schemas-microsoft-com:asm.v3">
<windowsSettings>
<!-- The combination of below two tags have the following effect :
1) Per-Monitor for >= Windows 10 Anniversary Update
2) System < Windows 10 Anniversary Update -->
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitor</dpiAwareness>
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware>
</windowsSettings>
</application>
If your app is targeting .NET Framework 4.6.2, you can ignore this step. Otherwise, you will also need to add the following snippet to app.config (inside <configuration> element):
<runtime>
<AppContextSwitchOverrides value = "Switch.System.Windows.DoNotScaleForDpiChanges=false"/>
</runtime>
Run on a PC with 2 monitors, Windows 10 Anniversary Update or higher. Set the 2 monitors DPI so that they are different (DPI of 100 on one and 150 on the other).
Doing the above steps will enable any WPF app, with pure WPF content, to work seamlessly as a Per Monitor DPI Aware app. There are several scenarios that will take some additional coding in your application with newly available APIs:
The scenario where a Win32 hosts WPF via HwndSource does not currently support Per Monitor DPI. DPI changed messages (WM_DPICHANGED) are only sent to the top level window, so WPF is not informed of a change. We’ll consider doing work in the future here, but it likely will require additional features from the Windows team.
The scenario where a WindowsForms app hosts WPF via ElementHost does not currently support Per Monitor DPI. We’ll consider doing work in the future here, but it likely will require additional features from the Windows team.
If you need to do more advanced coding, involving this feature, you’ll need to make sure that your app is targeting .NET 4.6.2.
Window.DpiChanged event:
as a window is moved to a different DPI monitor, this event will fire.Image.DpiChanged event:
as a window is moved to a different DPI monitor, this event will fire on all Images inside of that window. It is a routed event, so you could listen to it centrally, on the root element of each xaml file.WindowsFormHost.DpiChanged event:
as a window is moved to a different DPI monitor, this event will fire on all WindowsFormHosts inside of that window. It is a routed event, so you could listen to it centrally, on the root element of each xaml file.HwndHost.DpiChanged event:
as a window is moved to a different DPI monitor, this event will fire on all HwndHosts inside of that window. It is a routed event, so you could listen to it centrally, on the root element of each xaml file.HwndSource.DpiChanged event:
Most developers will be using the HwndSource that Window provides for them. If your application uses a HwndSource directly, you may find the following API useful:
DpiChanged event is fired on the HwndSource.
Note: During your event handler, if you mark this event as handled, WPF will not scale the UI for you, or notify the visuals of any DPI change.Visual.OnDpiChanged virtual method:
Each visual has a virtual method which can be overridden in order to listen to DPI change notifications on each visual. If your control needs to understand DPI for your rendering, you should override this method and behave appropriately.protected virtual void OnDpiChanged(DpiScale oldDpi, DpiScale newDpi)
/// <summary>
/// Gets the current DPI at which this visual is rendered.
/// </summary>
public static DpiScale GetDpi(Visual visual)
/// <summary>
/// This method updates the DPI information of a visual.
/// It can only be called on a Visual with no parent.
/// </summary>
public static void SetRootDpi(Visual visual, DpiScale dpiInfo)
If you use low level Text Formatting APIs and use custom TextSource, TextRun, GlyphRun, FormattedText to draw text in your app, then you need to use the following guidelines:
It has always been recommended that applications specify their DPI awareness using the application manifest rather than using the equivalent programmatic APIs. Use of the manifest forces the awareness to be fixed before any application code runs and avoids any ordering issues with respect to what code runs first at process startup (e.g. the DllMain in any statically referenced DLL will run before the application’s main). A new manifest element, <dpiAwareness>, has been added to the Windows 10 Anniversary Update. This element will supersede the older <dpiAware> element. The old element will still be supported for backwards compatibility. The new element allows you to indicate that you want different DPI awareness behavior for the current version of Windows than should be used on downlevel systems. For example, here is a manifest containing both the old and new DPI elements. This manifest will result in per-monitor DPI awareness on Win 10 Anniversary Update, and system DPI awareness for all earlier versions of windows.
<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</dpiAware>
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">
PerMonitor
</dpiAwareness>
</windowsSettings>
</application>
...
</assembly>
Characteristics of the new element:
Please let us know how this is working for you, or if you find any problems with the feature.
Email: [email protected]
We will update samples and this document as needed, look for updates.