I'm going to set up this question and answer it myself, so that others could search and find the correct answer easierly. I had to google for hours and compile the end result from multiple sources...
So the question is - how to enable Per-Monitor v2 DPI awareness in a ClickOnce scenario (WPF, c# specifically)?
Per-Monitor v2 was added in Win 10 Creators Update (1703). ClickOnce notoriously does not support DPI awareness declared in an app.manifest file, like this.
Per-Monitor v2 was added in Win 10 Creators Update (1703). ClickOnce notoriously does not support DPI awareness declared in an app.manifest file, like this. Show activity on this post. First of all, anyone who want's to cry out - simply target .NET 4.6.2, per monitor DPI awareness is enabled by default - that is simply not true.
The Per Monitor Aware WPF sample is a sample WPF application updated to be per-monitor DPI-aware. The sample consists of two projects: NativeHelpers.vcxproj: This is a native helper project that implements the core functionality to make a WPF application as per-monitor DPI-aware utilizing the Win32APIs above. The project contains two classes:
The Per Monitor Aware WPF sample application illustrates how a WPF application can be updated to be per-monitor DPI-aware by responding to the WM_DPICHANGED window notification. In response to the window notification, the sample updates the scale transform used by WPF based on the current DPI of the monitor the window is on.
If you have an existing WPF application and wish to leverage the DPI helper project from the sample to make it DPI aware, follow these steps. Start Visual Studio and select File > Open > Project/Solution Browse to the directory which contains an existing WPF application and double-click the Visual Studio Solution (.sln) file
First of all, anyone who want's to cry out - simply target .NET 4.6.2, per monitor DPI awareness is enabled by default - that is simply not true.
What is enabled by default in .NET 4.6.2 is the boilerplate code behind the scenes - quite nasty c++ hooks in window declaration code to enable support for per-monitor dpi awareness.
You still have to declare that you support per monitor dpi awareness via an app.manifest, but ClickOnce does not support it.
(do note that earlier versions of .NET will not support per monitor dpi awareness, even with the app manifest, unless you manually add the boilerplate code)
Now to the answer:
[assembly: DisableDpiAwareness]
. (this will require a using System.Windows.Media;
statement, simply click the light bulb that appears when hovering over the red squiggly line and add the suggested using statement)You will now need the following c# code somewhere in your project, I recommend creating a new static class for this:
internal static class NativeMethods
{
[DllImport("user32.dll", SetLastError = true)]
internal static extern bool SetProcessDpiAwarenessContext(int dpiFlag);
[DllImport("SHCore.dll", SetLastError = true)]
internal static extern bool SetProcessDpiAwareness(PROCESS_DPI_AWARENESS awareness);
[DllImport("user32.dll")]
internal static extern bool SetProcessDPIAware();
internal enum PROCESS_DPI_AWARENESS
{
Process_DPI_Unaware = 0,
Process_System_DPI_Aware = 1,
Process_Per_Monitor_DPI_Aware = 2
}
internal enum DPI_AWARENESS_CONTEXT
{
DPI_AWARENESS_CONTEXT_UNAWARE = 16,
DPI_AWARENESS_CONTEXT_SYSTEM_AWARE = 17,
DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE = 18,
DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 = 34
}
}
The final part is calling the above p/invoke methods and declaring dpi awareness support. We need to do this before any other code runs. For this, right-click app.xaml in SolutionExplorer and select View Code
. Then add this code:
protected override void OnStartup(StartupEventArgs e)
{
if (Environment.OSVersion.Version >= new Version(6, 3, 0)) // win 8.1 added support for per monitor dpi
{
if (Environment.OSVersion.Version >= new Version(10, 0, 15063)) // win 10 creators update added support for per monitor v2
{
NativeMethods.SetProcessDpiAwarenessContext((int)NativeMethods.DPI_AWARENESS_CONTEXT.DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
}
else NativeMethods.SetProcessDpiAwareness(NativeMethods.PROCESS_DPI_AWARENESS.Process_Per_Monitor_DPI_Aware);
}
else NativeMethods.SetProcessDPIAware();
base.OnStartup(e);
}
Make sure the rest of your code handles DPI properly. Starting with .NET 4.6.2 you can use the OnDpiChanged
event and VisualTreeHelper.GetDpi()
method.
Enjoy :)
@Marko - great answer! But here is one small detail: if you are mixing WinForms with WPF you have to add WinForms configuration. Please, update your answer, I don't have enough point to leave a comment...
<System.Windows.Forms.ApplicationConfigurationSection>
<add key="DpiAwareness" value="PerMonitorV2" />
</System.Windows.Forms.ApplicationConfigurationSection>
referal: https://docs.microsoft.com/en-us/dotnet/framework/winforms/high-dpi-support-in-windows-forms
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With