【发布时间】:2018-02-21 02:12:54
【问题描述】:
我目前正在处理我们的 WPF 应用程序中的一些高 DPI 问题(.NET 4.6.1 - 系统 DPI 感知处于活动状态)。
通常,该应用程序会按照我们的预期进行 - 根据当前显示 DPI 设置进行缩放,当将其从屏幕 A @ 100% 移动到屏幕 B @ 150% 时,它会正确更改其整体比例“在一半-观点”。
大多数未解决的问题是因为我们有一些基于像素/DIP 的计算没有考虑 DPI 设置。我通过计算正确的 DPI 值来解决这个问题:
var source = PresentationSource.FromVisual(this);
var dpiX = source?.CompositionTarget?.TransformToDevice.M11 ?? 1;
var dpiY = source?.CompositionTarget?.TransformToDevice.M22 ?? 1;
在那里我发现了第一件奇怪的事情(至少对我来说):
- 如果主显示设置为例如125% 对于所有屏幕,我得到 1.25
dpiX,甚至是 100% 的辅助屏幕,但是所有像素值已经乘以 1.25(意味着 1600x1200 像素屏幕的工作尺寸为 2000x1500)。 - 如果主屏幕处于 100% 而辅助屏幕处于例如150%:对于
dpiX,我总是得到 1,但所有值都已经正确,无需更正(=> 或乘以/除以 1 不会破坏它)。
但现在我的实际问题是:
我使用以下绑定将一些弹出窗口放置在其放置目标的中心:
<Popup.HorizontalOffset>
<MultiBinding Converter="{lth:CenterConverter}">
<Binding RelativeSource="{RelativeSource Self}" Path="PlacementTarget.ActualWidth" />
<Binding RelativeSource="{RelativeSource Self}" Path="Child.ActualWidth" />
<Binding RelativeSource="{RelativeSource Self}" Path="." />
</MultiBinding>
</Popup.HorizontalOffset>
和转换器:
public class CenterConverter : MarkupExtension, IMultiValueConverter
{
public override object ProvideValue(IServiceProvider serviceProvider) => this;
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (values.Any(v => v == DependencyProperty.UnsetValue))
return Double.NaN;
double placementTargetWidth = (double)values[0];
double elementWidth = (double)values[1];
var offset = (placementTargetWidth - elementWidth) / 2;
////if (values.Length >= 3 && values[2] is Visual)
////{
//// var source = PresentationSource.FromVisual((Visual)values[2]);
//// var dpiX = source?.CompositionTarget?.TransformToDevice.M11 ?? 1;
//// offset *= -1; //dpiX;
////}
return offset;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) { throw new NotSupportedException(); }
}
对于案例 2,在没有注释掉代码的情况下,一切都已经正常工作,但对于案例 1,我尝试将 DPI 值相乘,但最终正确的做法是将其乘以 -1 以使其正常工作.
为什么会这样?
我怎样才能有效地检测到什么时候需要呢? dpiX > 1?
我也对缩放问题或整个中心放置的其他解决方案持开放态度。
P.S.:我正在运行安装了 .NET 4.7 的 Windows 10 1703(由于某些其他原因,应用程序仍以 4.6.1 为目标)。
更新:
我创建了一个演示解决方案:https://github.com/chrfin/HorizontalOffsetError
如果主屏幕显示为 100% 则正确:
但如果主屏幕是例如125% 关闭:
但是,如果我将 *-1 添加到偏移量,它又是正确的:
...但是为什么呢?
【问题讨论】:
-
定位时为什么要处理dpi?测量实际尺寸(不要缩放)和位置。
-
@Sinatr:因为它在案例 1 中不起作用;-)。在这种情况下,弹出窗口会移到错误的一侧。示例:目标为 130 像素宽,弹出窗口为 230 像素 -> 偏移 = -50 像素 => 在案例 2 中将水平偏移设置为 -50 会将弹出窗口置于目标的中心,但对于案例 1,则为 100 像素off 而不是 0 或 50px,但将偏移量设置为 +50 会正确放置弹出窗口,我不知道为什么或如何检测何时 + 和何时 -....
-
@HansPassant:感谢您的输入,但我认为我不清楚那部分 - 如果只有一个屏幕,例如150% 或窗口不移动。一旦主(或唯一)屏幕不是 100%,问题似乎就会发生。明天我会尝试做一个小的演示解决方案......
标签: c# wpf scaling density-independent-pixel highdpi