美好的一天!
我已经在WPF应用程序上工作了一段时间(作为一种学习经历,哦,这是一次学习经历),它终于准备发布了。发布意味着将其安装在我的HTPC上,用于浏览我的电影收藏。
我在运行1920 * 1080但仍处于正常DPI设置的PC上进行设计,而HTPC / TV以相同的分辨率运行但DPI设置较高,原因显而易见。
问题是我的应用程序在HTPC上疯狂了,就像视觉效果一样搞乱了几乎所有东西。我知道这是由于糟糕的设计(mea culpa),但由于它是一个仅供我使用的应用程序,我正在寻找快速修复,而不是完全重新设计。我读过可以通过将以下内容添加到AssemblyInfo.cs来阻止应用程序识别DPI:
[assembly: System.Windows.Media.DisableDpiAwareness]
然而,它似乎没有任何影响,应用程序的行为保持不变。
有人能指出我正确的方向吗?
谢谢你, 约翰
答案 0 :(得分:32)
只是一些想法(未尝试过):
你在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的方法:
通过CompositionTarget
的{{1}}访问PresentationSource
以找出正在使用的DPI缩放功能.....您可以使用这些值进行一些缩放调整,即缩小你的“东西”(其大小/长度/等在设备无关单位中指定),因此当它由于更高的DPI生效而放大时它不会爆炸物理像素的使用(...这可以做到以各种方式,例如Window
,或对元素的宽度等进行计算。)
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
使用被解释为“原生”像素的位置,无论什么DPI缩放。
WPF for LCD screen Full HD
访问Canvas
上的TransFormToDevice
缩放矩阵,并从中计算出一个新矩阵,该矩阵只撤消该缩放并在CompositionTarget
或LayoutTransform
中使用该矩阵
使用一种方法(甚至可以把它放在转换器中)明确修改DIP(设备独立位置)位置/长度....如果你希望你的窗口大小匹配你可能会这样做特定的像素大小。
答案 1 :(得分:6)
the documentation解释了如何使用This blog article子类来做到这一点。
简而言之,创建一个这样的类:
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"
之类的内容,然后用<custom:DpiDecorator>
... </custom:DpiDecorator>
包含与DPI无关的用户界面。
答案 2 :(得分:3)
以上都没有真正禁用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的列表中。因此,您需要将这些值初始化为正确的值。
以下是我自己使用的示例代码(仅适用于.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);
答案 3 :(得分:2)
快速修复只是将您的Window内容包装在Viewbox中。如果Viewbox的直接子节点具有明确的宽度和高度,并且两台机器上的宽高比相同,那么这应该可以立即启动并运行。
答案 4 :(得分:2)
DpiAwareness
仅影响DPI虚拟化。在Windows中,它在Windows 7和Windows中显示为“使用Windows XP样式缩放”。 8自定义分辨率框,其中选中表示已禁用,未选中表示已启用。 (如果你改变它,请记住在主字体大小的屏幕上应用!好的方框是不够的。)
如果您关闭了DPI虚拟化,那么它会使您的应用程序告诉它支持的操作系统没有区别,它将始终使用标准缩放。当它打开时,它会区分真正的缩放(感知)和在整个应用程序上调整双线性调整大小(不知道)。
我记得另一个警告,如果没有Aero Glass工作,DPI virt将无法运作。
答案 5 :(得分:0)
答案 6 :(得分:0)
Calin Pirtea的答案确实使DPI缩放无效。
对于那些希望将应用程序的显示比例缩放到100%以上并接受模糊的人,Carlos Borau的答案很有效。这是.NET 4.6(C#)的一种方法:
1将清单文件添加到您的应用程序项目
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>
答案 7 :(得分: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);
}
}