关于此问题中报告的问题:
一个设计上不支持 DPI 的应用程序,依靠 Windows 虚拟化来扩展其 UI 内容,突然(尽管经过一些修改,导致一个小的版本更新) - 显然没有明显的原因 - 变为 DPI-Aware(系统感知)。
由于 DPI 感知已成为 UI 呈现的一个相关方面,鉴于可用屏幕分辨率的多样性(以及相关的 DPI 缩放设置),大多数组件生产商已经适应了高 DPI 并且他们的产品是 DPI 感知的(当缩放检测到 DPI 更改)并使用 DPI-Aware 程序集(通常引用 WPF 程序集,定义为 DPI-Aware)。
当项目中(直接或间接)引用这些 DPI-Aware 组件之一时,如果 DPI-Awareness 尚未明确禁用,则 DPI-Unaware 应用程序将变为 DPI-Aware。
声明程序集 DPI-Awareness 的更直接(和推荐)的方法是在应用程序清单中显式声明它。
有关 Visual Studio 2017 之前的应用程序清单设置,请参阅 Hans Passant 的回答:
How to configure an app to run on a machine with a high DPI setting
自 Visual Studio 2015-Upd.1 起,此设置已存在于 app.manifest 中,只需取消注释即可。设置部分:<dpiAware>false</dpiAware>。
<?xml version="1.0" encoding="utf-8"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
<assemblyIdentity version="1.0.0.0" name="MyApplication.app"/>
//(...)
<!-- Indicates that the application is DPI-aware and will not be automatically scaled by Windows at higher
DPI. Windows Presentation Foundation (WPF) applications are automatically DPI-aware and do not need
to opt in. Windows Forms applications targeting .NET Framework 4.6 that opt into this setting, should
also set the 'EnableWindowsFormsHighDpiAutoResizing' setting to 'true' in their app.config. -->
<application xmlns="urn:schemas-microsoft-com:asm.v3">
<windowsSettings>
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">false</dpiAware>
</windowsSettings>
</application>
//(...)
</assembly>
有关更多信息,请参阅这些 MSDN 文章:
High DPI desktop application development on Windows
Setting the default DPI awareness for a process
另一种方法是使用这些 Windows API 函数设置进程上下文 DPI-Awareness:
Windows 7
SetProcessDPIAware
[DllImport("user32.dll", SetLastError=true)]
static extern bool SetProcessDPIAware();
Windows 8.1
SetProcessDpiAwareness
[DllImport("shcore.dll")]
static extern int SetProcessDpiAwareness(ProcessDPIAwareness value);
enum ProcessDPIAwareness
{
DPI_Unaware = 0,
System_DPI_Aware = 1,
Per_Monitor_DPI_Aware = 2
}
Windows 10,版本 1703
SetProcessDpiAwarenessContext()
(When opting for a Per-Monitor DPI-Awareness, use Context_PerMonitorAwareV2)
另见:Mixed-Mode DPI Scaling and DPI-aware APIs - MSDN
Windows 10 版本 1809(2018 年 10 月)
添加了一个新的DPI_AWARENESS_CONTEXT:DPI_AWARENESS_CONTEXT_UNAWARE_GDISCALED
DPI 不知道基于 GDI 的内容质量有所提高。这种模式
行为类似于 DPI_AWARENESS_CONTEXT_UNAWARE,但也启用
系统自动提高文本的渲染质量和
当窗口以高 DPI 显示时其他基于 GDI 的基元
监控。
使用GetWindowDpiAwarenessContext() 函数检索窗口的DPI_AWARENESS_CONTEXT 句柄,并使用GetThreadDpiAwarenessContext() 检索当前线程的DPI_AWARENESS_CONTEXT 句柄。然后GetAwarenessFromDpiAwarenessContext() 从DPI_AWARENESS_CONTEXT 结构中检索DPI_AWARENESS 值。
[DllImport("user32.dll", SetLastError=true)]
static extern IntPtr GetWindowDpiAwarenessContext(IntPtr hWnd);
[DllImport("user32.dll", SetLastError=true)]
static extern IntPtr GetThreadDpiAwarenessContext();
[DllImport("user32.dll", SetLastError=true)]
static extern int GetAwarenessFromDpiAwarenessContext(IntPtr DPI_AWARENESS_CONTEXT);
[DllImport("user32.dll", SetLastError=true)]
static extern int SetProcessDpiAwarenessContext(DpiAwarenessContext value);
// Virtual enumeration: DPI_AWARENESS_CONTEXT is *contextual*.
// This value is returned by GetWindowDpiAwarenessContext() or GetThreadDpiAwarenessContext()
// and finalized by GetAwarenessFromDpiAwarenessContext(). See the Docs.
enum DpiAwarenessContext
{
Context_Undefined = 0,
Context_Unaware = -1,
Context_SystemAware = -2,
Context_PerMonitorAware = -3,
Context_PerMonitorAwareV2 = -4,
Context_UnawareGdiScaled = -5
}
由于 DPI-Awareness 是基于线程的,因此这些设置可以应用于特定线程。这在重新设计用户界面以实现 DPI 感知时非常有用,可以让系统扩展不太重要的组件,同时专注于更重要的功能。
SetThreadDpiAwarenessContext
(与SetProcessDpiAwarenessContext()相同的参数)
Assemblyinfo.cs
如果引用 WPF 程序集的第三方/外部组件重新定义了应用程序的 DPI-Awareness 状态,则可以禁用此自动行为,在项目的Assemblyinfo.cs 中插入参数:
[assembly: System.Windows.Media.DisableDpiAwareness]