如何计算其已知字体大小和字符的WPF TextBlock宽度?
比方说,我有TextBlock
与文本“一些文本”和字体大小10.0 。
我如何计算适当的TextBlock
宽度 ?
使用FormattedText
类。
我在我的代码中做了一个帮助函数:
private Size MeasureString(string candidate) { 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); return new Size(formattedText.Width, formattedText.Height); }
为了logging…我假定op'er正在试图以编程方式确定textBlock将被添加到视觉树后将占用的宽度。 国际海事组织一个更好的解决scheme,然后formattedText(你如何处理像textWrapping的东西?)将使用测量和排列在示例TextBlock。 例如
var textBlock = new TextBlock { Text = "abc abd adfdfd", TextWrapping = TextWrapping.Wrap }; // auto sized textBlock.Measure(new Size(Double.PositiveInfinity, Double.PositiveInfinity)); textBlock.Arrange(new Rect(textBlock.DesiredSize)); Debug.WriteLine(textBlock.ActualWidth); // prints 80.323333333333 Debug.WriteLine(textBlock.ActualHeight);// prints 15.96 // constrain the width to 16 textBlock.Measure(new Size(16, Double.PositiveInfinity)); textBlock.Arrange(new Rect(textBlock.DesiredSize)); Debug.WriteLine(textBlock.ActualWidth); // prints 14.58 Debug.WriteLine(textBlock.ActualHeight);// prints 111.72
我通过向后端代码中的元素添加绑定path来解决此问题:
<TextBlock x:Name="MyText" Width="{Binding Path=ActualWidth, ElementName=MyText}" />
我发现这是一个非常干净的解决scheme,比将FormattedText的所有开销添加到我的代码中更加简单。
之后,我能够做到这一点:
double d_width = MyText.Width;
我发现了一些方法工作正常…
/// <summary> /// Get the required height and width of the specified text. Uses Glyph's /// </summary> public static Size MeasureText(string text, FontFamily fontFamily, FontStyle fontStyle, FontWeight fontWeight, FontStretch fontStretch, double fontSize) { Typeface typeface = new Typeface(fontFamily, fontStyle, fontWeight, fontStretch); GlyphTypeface glyphTypeface; if (!typeface.TryGetGlyphTypeface(out glyphTypeface)) { return MeasureTextSize(text, fontFamily, fontStyle, fontWeight, fontStretch, fontSize); } double totalWidth = 0; double height = 0; for (int n = 0; n < text.Length; n++) { ushort glyphIndex = glyphTypeface.CharacterToGlyphMap[text[n]]; double width = glyphTypeface.AdvanceWidths[glyphIndex] * fontSize; double glyphHeight = glyphTypeface.AdvanceHeights[glyphIndex] * fontSize; if (glyphHeight > height) { height = glyphHeight; } totalWidth += width; } return new Size(totalWidth, height); } /// <summary> /// Get the required height and width of the specified text. Uses FortammedText /// </summary> public static Size MeasureTextSize(string text, FontFamily fontFamily, FontStyle fontStyle, FontWeight fontWeight, FontStretch fontStretch, double fontSize) { FormattedText ft = new FormattedText(text, CultureInfo.CurrentCulture, FlowDirection.LeftToRight, new Typeface(fontFamily, fontStyle, fontWeight, fontStretch), fontSize, Brushes.Black); return new Size(ft.Width, ft.Height); }
提供的解决scheme适用于.Net Framework 4.5,但是,随着Windows 10 DPI缩放和Framework 4.6.x添加不同程度的支持,用于测量文本的构造函数现在被标记为[Obsolete]
以及任何构造函数方法,不包含pixelsPerDip
参数。
不幸的是,它涉及更多一点,但是新的扩展能力会带来更高的精度。
PixelsPerDip
根据MSDN,这代表:
像素密度独立像素值,这是比例因子的等价物。 例如,如果屏幕的DPI是120(或1.25,因为120/96 = 1.25),则绘制每个密度独立像素1.25个像素。 DIP是WPF使用的独立于设备分辨率和DPI的度量单位。
这是我根据来自Microsoft / WPF-Samples GitHub存储库的DPI扩展意识指导实施的选定答案。
还有一些额外的configuration需要完全支持从Windows 10周年纪念(下面的代码)DPI缩放,我不能工作,但没有它可以在一个configuration了缩放(并尊重缩放更改)的单个监视器上工作。 上面的回购中的Word文档是该信息的来源,因为一旦我添加了这些值,我的应用程序就不会启动。 这个来自同一个仓库的示例代码也是一个很好的参考点。
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) { DpiInfo 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在多显示器configuration中每个显示器具有不同DPI设置的function。 这是一个公正的猜测,没有这些设置,OnDpiChanged事件可能不会引发一个窗口从一个显示移动到另一个不同的设置,这将使您的测量继续依赖于以前的DpiScale
。 我写的这个应用程序对我来说是独一无二的,而且我没有这种设置,所以我没有什么可以testing的,当我遵循指导原则时,我最终得到了一个无法启动的应用程序错误,所以我放弃了,但是最好把它看做是一个好主意,并且调整你的应用程序清单来包含:
<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)系统<Windows 10周年更新
为你find这个:
Graphics g = control.CreateGraphics(); int width =(int)g.MeasureString(aString, control.Font).Width; g.dispose();