How to Read the Current Path of |Datadirectory| from Config Settings

WPF ClickOnce DPI awareness Per-Monitor v2

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:

  1. Make sure your project targets .NET 4.6.2. (easisest to use Visual Studio 2017 for this)
  2. Disable DPI awareness in the assembly properties. Trust me on this, we shall re-enable it later in code. To do this, open AssemblyInfo.cs under your project Properties node (expand the spanner icon inside the SolutionExplorer, usually on the right side). Add the following code to the very last line: [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)
  3. Add an app.manifest file and declare support for Win 10 and other OS versions. Right-click your project in SolutionExplorer -> add -> new item -> Application manifest file. Open the created manifest file and in the middle there is a section with OS versions, uncomment them as such:
    OS versions in app.manifest
    Do not uncomment the section about DPI awareness (a bit further down)! ClickOnce will throw errors if you do.
  4. 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
    }
    }
  5. 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);
    }
  6. 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 :)

Does WPF Per Monitor DPI awareness really work by default?

As @emoacht has commented, using "PerMonitorV2" instead of just "PerMonitor" makes WPF applications automatically Per monitor DPI aware for Windows 10 (after a certain build number)

In case you have converted your application from an older .Net version and just changed the project to SDK Style, don't forget to mention the manifest in the project file.

enter image description here

Is Per-Monitor v2 Awareness supported by Windows Server?

According to the Microsoft documentation it is only available in client Windows versions.

Minimum supported client : Windows 10, version 1607 [desktop apps only]

Minimum supported server : None supported

Microsoft store certification fails due to DPI awareness

Finally I found the solution to the issue:
Instead of trying to make my app DPI aware from the code by calling the corresponding methods from a system DLL, I had to add an app.manifest file to the WPF project. As I tried this way the issue related to the invalid screen size disappeared.

Here is an example what should contain this manifest file. A very important thing is that the Store didn't accepted the PerMonitor setting as it is in the example above. It must be changed to PerMonitorV2 (by the way this is what they asked in the error message as well).

By adding this settings to the project, it passed the certification on the Store. And surprisingly yes, this warning was the reason why the certification failed.

One more thing after adding the manifest file, it should be connected to the project. You can do it by adding the following code to the <your_project_name>.csproj file (example here):

<PropertyGroup>
<ApplicationManifest>app.manifest</ApplicationManifest>
</PropertyGroup>

To see the whole picture, I also added the second step from this answer.

Enjoy!

Scaling the non-client area (title bar, menu bar) for per-monitor high-DPI support

In any up-to-date Windows Insider builds (build >= 14342, SDK version# >= 14332) there is an EnableNonClientDpiScaling API (which takes an HWND as its argument) that will enable non-client DPI scaling for top-level HWNDs. This functionality requires that the top-level window be running in per-monitor DPI-awareness mode. This API should be called from the WM_NCCREATE handler for the window. When this API is called on a top-level window, its caption bar, top-level scrollbars, system menu and menubar will DPI scale when the application’s DPI changes (this can happen when the app is moved to a display with a different display scaling value or when the DPI changes for other reasons such as the user making a settings change or when an RDP connection changes the scale factor).

This API does not support scaling non-client area for child windows, such as scroll bars in a child window.

To my knowledge there is no way to have non-client area DPI scale automatically without this API.

Note that this API has not yet been finalized, and it may change prior to being released in the Windows 10 Anniversary update. Keep your eye on MSDN for official documentation when it becomes final.



Related Topics



Leave a reply



Submit