有没有办法禁用所有 WPF 应用程序的缩放,即使窗口在每个监视器或系统范围具有一定的缩放



我需要具有始终与物理像素 1:1 的 WPF 窗口(DPI 始终为 96(,此时我发现没有办法做到这一点:用于感知的应用程序清单或 API 方法毫无用处,它们只是改变了内容的缩放方式:作为位图或由应用程序缩放。我需要应用程序始终具有固定的 100% 比例,即使每个显示器或整个系统的系统设置为 200%。你们知道一些可以提供帮助的方法吗?

对于那些将寻找类似解决方案的人:

我发现没有办法强制应用程序不与操作系统一起缩放,但找到了如何保持 1:1 缩放的方法。

您需要做的第一件事是定义应用程序是 dpi 感知的,因此它不会由系统扩展,而是由应用程序本身扩展。下面是 app.manifest 代码:

<?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"/>
  <application xmlns="urn:schemas-microsoft-com:asm.v3">
    <windowsSettings>
      <dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true/PM</dpiAware>
    </windowsSettings>
  </application>
</assembly>

然后,你将能够获取视觉对象的当前比例:

double xScale = PresentationSource.FromVisual(someVisual).CompositionTarget.TransformToDevice.M11;
double yScale = PresentationSource.FromVisual(someVisual).CompositionTarget.TransformToDevice.M22;

然后,您可以将布局转换绑定到控件(在我的例子中为图像(以取消缩放效果:

<Image.LayoutTransform>
    <ScaleTransform ScaleX="{Binding ElementName=_this, Path=ScaleX}"
                    ScaleY="{Binding ElementName=_this, Path=ScaleY}" />
</Image.LayoutTransform>

请注意,比例应反转,因此 ScaleX 和 ScaleY 将具有以下值:

ScaleX = 1 / scaleX;
ScaleY = 1 / scaleY;

据我所知,您可以通过在应用程序清单中声明True/PM来抑制所有自动缩放,这意味着应用程序对缩放负全部责任,然后在出现时忽略WM_DPICHANGED消息。

<application xmlns="urn:schemas-microsoft-com:asm.v3">
  <windowsSettings>
    <dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">True/PM</dpiAware>
  </windowsSettings>
</application>
public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }
    protected override void OnSourceInitialized(EventArgs e)
    {
        base.OnSourceInitialized(e);
        var source = PresentationSource.FromVisual(this) as HwndSource;
        source?.AddHook(WndProc);
    }
    private const int WM_DPICHANGED = 0x02E0;
    private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
    {
        if (msg == WM_DPICHANGED)
        {
            handled = true;
        }
        return IntPtr.Zero;
    }
}

我不太确定用户是否可以通过任何操作系统设置覆盖此行为。

最新更新