28

再会!

我已经在 WPF 应用程序上工作了一段时间(作为一种学习体验,哦,天哪,这是一种学习体验),它终于准备好发布了。发布意味着将其安装在我的 HTPC 上,用于浏览我的电影收藏。

我在运行 1920*1080 但处于正常 DPI 设置的 PC 上设计了它,而 HTPC/TV 运行在相同的分辨率但出于显而易见的原因更高的 DPI 设置。

问题是我的应用程序在 HTPC 上变得疯狂,就视觉效果而言,几乎所有东西都搞砸了。我知道这是由于糟糕的设计(mea culpa),但由于它是一个仅供我使用的应用程序,我正在寻找快速修复,而不是完全重新设计。我读到可以通过将以下内容添加到 AssemblyInfo.cs 来阻止应用程序感知 DPI:

[assembly: System.Windows.Media.DisableDpiAwareness]

但是,它似乎没有任何效果,并且应用程序的行为保持不变。

谁能指出我正确的方向?

谢谢你,约翰

4

9 回答 9

37

DPIA意识

只是一些想法(未尝试):

你在XP上运行吗?该选项可能不适用于该平台。

以下可能只是设置相同 DpiAwareness 选项的不同方法:

  • 查看EXE上的“兼容模式”设置...右键单击它的属性...然后打开“禁用显示缩放”

  • 创建一个清单,并说你不知道

    <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3" >
     ...
      <asmv3:application>
        <asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
          <dpiAware>false</dpiAware>
        </asmv3:windowsSettings>
      </asmv3:application>
     ...
    </assembly>
    
  • 调用SetProcessDPIAware(认为你必须尽早调用,即在创建 Window 之前)

    http://msdn.microsoft.com/en-gb/library/windows/desktop/ms633543 (v=vs.85).aspx

您可以致电IsProcessDPIAware检查您的应用程序进程是否已应用该设置。

找出 DPI

以下是您了解当前使用的 DPI 的方法:

CompositionTarget通过PresentationSource您的访问Window以了解它正在使用什么 DPI 缩放.....然后您可以使用这些值进行一些缩放调整,即缩小您的“东西”(其尺寸/长度/等在设备独立单位中指定) ,因此当由于更高的 DPI 生效而按比例放大时,它不会爆炸物理像素的使用(...这可以通过各种方式完成,例如ViewBox,或计算元素的宽度等)。

double dpiX, double dpiY;
PresentationSource presentationsource = PresentationSource.FromVisual(mywindow);

if (presentationsource != null) // make sure it's connected
{
    dpiX = 96.0 * presentationsource.CompositionTarget.TransformToDevice.M11;
    dpiY = 96.0 * presentationsource.CompositionTarget.TransformToDevice.M22;
}

做缩放调整

  • 使用ViewBox技巧

    请参阅我之前提出的这个答案,Canvas无论 DPI 缩放如何,都允许使用被解释为“本机”像素的位置。

    液晶屏全高清的WPF

  • 访问 上的TransFormToDevice缩放矩阵CompositionTarget,并从中计算一个新矩阵,该矩阵只是撤消该缩放并在LayoutTransformor中使用它RenderTransform

  • 使用一种方法(甚至可能将其放入转换器中),以显式修改 DIP(设备独立位置)位置/长度....如果您希望窗口大小与特定像素大小匹配,您可以这样做。

于 2012-12-13T11:36:41.460 回答
10

这篇博客文章解释了如何使用装饰器子类来做到这一点。

简而言之,创建一个这样的类:

namespace MyNamespace
{
    public class DpiDecorator : Decorator
    {
        public DpiDecorator()
        {
            this.Loaded += (s, e) =>
            {
                Matrix m = PresentationSource.FromVisual(this).CompositionTarget.TransformToDevice;
                ScaleTransform dpiTransform = new ScaleTransform(1 / m.M11, 1 / m.M22);
                if (dpiTransform.CanFreeze)
                    dpiTransform.Freeze();
                this.LayoutTransform = dpiTransform;
            };
        }
    };
};

然后xmlns:custom="clr-namespace:MyNamespace"在您的顶级窗口定义中添加类似的内容,然后将您的 DPI 独立用户界面与<custom:DpiDecorator>...括起来</custom:DpiDecorator>

于 2016-03-02T01:12:43.403 回答
4

以上都没有真正禁用 WPF 中的 dpi 缩放。

这是 WPF 在 .net 4.6 中计算 dpi 缩放的方式:1) HwndTarget,它是所有视觉对象使用的 CompositionTarget。2) UIElement 是视觉效果的基类,也是存储dpi缩放计算结果的地方。

WPF 确保在准备第一个视觉对象时计算一次全局缩放。这由 HwndTarget {private static bool _setDpi = true} 上的布尔值控制。计算 dpi 后,_setDpi 字段设置为 false,并且 dpi 感知方法是使用像这样的代码的快捷方式 {if (!HwndTarget._setDpi) return;}

每个 UIElement 都会发生类似的事情,它具有相同的模式,使用 {private static bool _setDpi = true} 允许第一次计算。

下一个检查来自 ProcessDpiAwareness,它可以是 None、Process 或 Monitor。为了阻止 WPF 考虑单个监视器,您需要将 ProcessDpiAwareness 设置为 Process (int 1)。

最后,当计算 dpi 时,结果存储在 UIElement 上称为 DpiScaleXValues 和 DpiScaleYValues 的 2 个列表中。因此,您需要将这些初始化为正确的值。

这是我自己使用的示例代码(仅适用于 .net 4.6):

        var setDpiHwnd = typeof(HwndTarget).GetField("_setDpi", BindingFlags.Static | BindingFlags.NonPublic);
        setDpiHwnd?.SetValue(null, false);

        var setProcessDpiAwareness = typeof(HwndTarget).GetProperty("ProcessDpiAwareness", BindingFlags.Static | BindingFlags.NonPublic);
        setProcessDpiAwareness?.SetValue(null, 1, null);

        var setDpi = typeof(UIElement).GetField("_setDpi", BindingFlags.Static | BindingFlags.NonPublic);

        setDpi?.SetValue(null, false);

        var setDpiXValues = (List<double>)typeof(UIElement).GetField("DpiScaleXValues", BindingFlags.Static | BindingFlags.NonPublic)?.GetValue(null);

        setDpiXValues?.Insert(0, 1);

        var setDpiYValues = (List<double>)typeof(UIElement).GetField("DpiScaleYValues", BindingFlags.Static | BindingFlags.NonPublic)?.GetValue(null);

        setDpiYValues?.Insert(0, 1);
于 2017-05-25T02:46:34.253 回答
3

一个快速的解决方法就是将 Window 的内容包装在Viewbox中。如果 Viewbox 的直接子项具有明确的宽度和高度,并且如果两台机器上的纵横比相同,那么这应该可以让您立即启动并运行。

于 2012-12-13T13:16:53.400 回答
2

DpiAwareness仅影响 DPI 虚拟化。在 Windows 中,它在 Windows 7 和 8 自定义分辨率框中显示为“使用 Windows XP 样式缩放”,其中选中表示禁用,未选中表示启用。(如果您更改它,请记住在主字体大小屏幕上应用!确定框是不够的。)

如果您关闭了 DPI 虚拟化,那么您的应用程序告诉它支持的操作系统的内容将产生零差异,它将始终使用标准缩放。当它打开时,它会在真正的缩放(感知)和只是对整个应用程序(不感知)进行双线性调整之间产生差异。

我只记得另一个警告,如果没有 Aero Glass 工作,DPI virt 根本无法工作。

于 2013-02-28T02:52:47.593 回答
1

Calin Pirtea 的回答真正禁用了 DPI 缩放。

对于那些想要将应用程序拉伸到 100% 以上并接受模糊的人来说,Carlos Borau 的答案很有效。这是 .NET 4.6 (C#) 的一种方式:

1 将清单文件添加到您的应用程序项目中

  1. 右键项目,添加->新建项目
  2. 选择常规->应用程序清单文件

2 取消注释并设置 dpiAware = false:

  <application xmlns="urn:schemas-microsoft-com:asm.v3">
      <windowsSettings>
          <dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">false</dpiAware>
      </windowsSettings>
  </application>
于 2018-08-28T03:17:46.073 回答
0

对于那些使用 VB.NET 的人来说,访问 app.manifest 的方法是:

在此处输入图像描述

然后取消注释这部分并将其设置为“false”

在此处输入图像描述

于 2017-10-10T11:41:56.577 回答
0

在 Windows 10 上更新到 .NET 4.8 运行时后,Calin 设置 ProcessDpiAwareness 的方法出现以下异常

System.ArgumentException:“'System.Int32' 类型的对象无法转换为'System.Nullable'1 [MS.Win32.NativeMethods+PROCESS_DPI_AWARENESS]'类型。”

将代码更改为以下可以解决异常,但我不确定它是否能达到相同的结果。

var processDpiAwareness = typeof(HwndTarget).GetProperty("ProcessDpiAwareness", BindingFlags.Static | BindingFlags.NonPublic);

if (processDpiAwareness != null)
{
      var underlyingType = Nullable.GetUnderlyingType(processDpiAwareness.PropertyType);

      if (underlyingType != null)
      {
           var val = Enum.Parse(underlyingType, "PROCESS_SYSTEM_DPI_AWARE");
           processDpiAwareness.SetValue(null, val, null);
      }
 }
于 2019-08-22T00:39:24.837 回答
0

就我而言,更改我的 exe 的高 DPI 设置会有所帮助。下面的例子。我在组合框中选择“应用程序”。

在此处输入图像描述

在此处阅读有关它的更多信息

于 2022-02-09T13:44:06.940 回答