我该如何计算合适的TextBlock宽度?
使用FormattedText
类。
我在我的代码中创建了一个辅助函数:
private Size MeasureString(string candidate)
{
var formattedText = new FormattedText(
candidate,
CultureInfo.CurrentCulture,
FlowDirection.LeftToRight,
new Typeface(this.textBlock.FontFamily, this.textBlock.FontStyle, this.textBlock.FontWeight, this.textBlock.FontStretch),
this.textBlock.FontSize,
Brushes.Black,
new NumberSubstitution(),
VisualTreeHelper.GetDpi(this.textBlock).PixelsPerDip);
return new Size(formattedText.Width, formattedText.Height);
}
它返回可用于 WPF 布局的设备无关像素。
formattedText.MaxTextHeight = textBlock.MaxHeight;
formattedText.MaxTextWidth = textBlock.MaxWidth;
formattedText.Trimming = TextTrimming.None; // 如果有修剪,请更改此值
- gil123就记录而言,我假设操作者正在尝试通过编程确定将文本块添加到可视树后占用的宽度。在我看来,比使用格式化文本(如何处理文本换行?)更好的解决方案是在示例TextBlock上使用Measure和Arrange。
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
[Obsolete]
,以及该方法上未包括pixelsPerDip
参数的任何构造函数。在Windows 10周年更新中,为了完全支持DPI缩放,还需要进行一些额外的配置(以下是代码),但我无法使其正常工作。不过,即使没有这些配置,它也可以在单个显示器上配置缩放并且能够遵循缩放更改。以上存储库中的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)
{
DpiScale dpiInfo;
lock (m_sync)
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周年纪念版在多显示器配置中具有不同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 周年更新及以上版本,为 Per-Monitor 2)对于低于 Windows 10 周年更新版本,为 System
DpiScale
信息存储在字段中?为什么不使用var dpiInfo = VisualTreeHelper.GetDpi(this.textBlock)
?性能?线程?有人可以帮我理解吗? - mike我发现一些方法可以很好地运作...
/// <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);
}
在后端代码中为元素添加一个绑定路径即可解决此问题:
<TextBlock x:Name="MyText" Width="{Binding Path=ActualWidth, ElementName=MyText}" />
我发现这是一种比将诸如FormattedText之类的参考内容添加到我的代码中更为简洁的解决方案。
然后,我能够做到这一点:
double d_width = MyText.Width;
d_width = MyText.ActualWidth;
。问题出现在当 TextBlock
尚未出现在可视树中时。 - xmedekovar typeface = new Typeface(textBlock.FontFamily, textBlock.FontStyle, textBlock.FontWeight, textBlock.FontStretch);
var formattedText = new FormattedText(textBlock.Text, Thread.CurrentThread.CurrentCulture, textBlock.FlowDirection, typeface, textBlock.FontSize, textBlock.Foreground);
var size = new Size(formattedText.Width, formattedText.Height)
为您找到了这个:
Graphics g = control.CreateGraphics();
int width =(int)g.MeasureString(aString, control.Font).Width;
g.dispose();
ActualWidth
获取实际宽度。 - H.B.