提供的解决方案适用于 .Net Framework 4.5,但是,随着 Windows 10 DPI 缩放和 Framework 4.6.x 为其添加不同程度的支持,用于测量文本的构造函数现在标记为 [Obsolete],以及任何该方法上不包含 pixelsPerDip 参数的构造函数。
不幸的是,它涉及更多一点,但它会通过新的缩放功能提高准确性。
###PixelsPerDip
根据 MSDN,这表示:
Pixels Per Density Independent Pixel 值,相当于比例因子。例如,如果屏幕的 DPI 为 120(或 1.25,因为 120/96 = 1.25),则每个密度独立像素绘制 1.25 个像素。 DIP 是 WPF 使用的独立于设备分辨率和 DPI 的测量单位。
这是我根据Microsoft/WPF-Samples GitHub 存储库的指导对所选答案的实现,并具有 DPI 缩放意识。
需要一些额外的配置才能完全支持 Windows 10 周年纪念版的 DPI 缩放(代码下方),我无法开始工作,但没有它,它可以在单个显示器上工作配置了缩放(并尊重缩放更改)。上述 repo 中的 Word 文档是该信息的来源,因为一旦我添加了这些值,我的应用程序就不会启动。来自同一个仓库的This sample code 也是一个很好的参考点。
public partial class MainWindow : Window
{
private DpiScale m_dpiInfo;
private readonly object m_sync = new object();
public MainWindow()
{
InitializeComponent();
Loaded += OnLoaded;
}
private Size MeasureString(string candidate)
{
DpiScale dpiInfo;
lock (m_dpiInfo)
dpiInfo = m_dpiInfo;
if (dpiInfo == null)
throw new InvalidOperationException("Window must be loaded before calling MeasureString");
var formattedText = new FormattedText(candidate, CultureInfo.CurrentUICulture,
FlowDirection.LeftToRight,
new Typeface(this.textBlock.FontFamily,
this.textBlock.FontStyle,
this.textBlock.FontWeight,
this.textBlock.FontStretch),
this.textBlock.FontSize,
Brushes.Black,
dpiInfo.PixelsPerDip);
return new Size(formattedText.Width, formattedText.Height);
}
// ... The Rest of Your Class ...
/*
* Event Handlers to get initial DPI information and to set new DPI information
* when the window moves to a new display or DPI settings get changed
*/
private void OnLoaded(object sender, RoutedEventArgs e)
{
lock (m_sync)
m_dpiInfo = VisualTreeHelper.GetDpi(this);
}
protected override void OnDpiChanged(DpiScale oldDpiScaleInfo, DpiScale newDpiScaleInfo)
{
lock (m_sync)
m_dpiInfo = newDpiScaleInfo;
// Probably also a good place to re-draw things that need to scale
}
}
其他要求
根据 Microsoft/WPF-Samples 上的文档,您需要向应用程序的清单中添加一些设置,以涵盖 Windows 10 Anniversary 在多显示器配置中为每个显示器设置不同 DPI 设置的能力。可以公平地猜测,如果没有这些设置,当窗口从一个显示器移动到另一个具有不同设置的显示器时,可能不会引发 OnDpiChanged 事件,这将使您的测量继续依赖于之前的DpiScale。我正在编写的应用程序是为我一个人编写的,我没有那种设置,所以我没有什么可测试的,当我按照指导进行时,我最终得到了一个由于清单而无法启动的应用程序错误,所以我放弃了,但最好检查一下并调整您的应用清单以包含:
<application xmlns="urn:schemas-microsoft-com:asm.v3">
<windowsSettings>
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware>
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitor</dpiAwareness>
</windowsSettings>
</application>
根据文档:
[这些]两个标签的组合具有以下效果:
1) >= Windows 10 周年更新的每个监视器
2) 系统