WPF 通过功能丰富的用户界面(UI)控件支持文本内容的呈现。 一般情况下,可以在三个层中划分文本呈现:
使用 FormattedText 对象。
使用高级控件,例如 TextBlock 和 FlowDocument 对象。
本主题提供文本呈现性能建议。
字形级别的呈现文本
Windows Presentation Foundation (WPF) 为希望在格式化后截取和保留文本的客户提供高级文本支持,包括可直接访问 Glyphs 的字形级标记。 这些功能为以下每种方案中的不同文本呈现要求提供关键支持。
固定格式文档的屏幕显示。
打印方案。
可扩展应用程序标记语言(XAML)作为设备打印机语言。
Microsoft XPS 文档编写器。
以前的打印机驱动程序,从 Win32 应用程序输出到固定格式。
打印后台处理格式。
固定格式的文档演示,包括以前版本的 Windows 客户端和其他计算设备。
注释
Glyphs 和 GlyphRun 专为固定格式的文档演示和打印方案而设计。 WPF 为常规布局和用户界面(UI)方案(如 Label 和 TextBlock)提供了多个元素。 有关布局和 UI 方案的详细信息,请参阅 WPF中的
以下示例演示如何在 XAML 中定义 Glyphs 对象的属性。 这些示例假定本地计算机上的 C:\WINDOWS\Fonts 文件夹中安装了 Arial、Courier New 和 Times New Roman 字体。
<!-- The example shows how to use a Glyphs object. -->
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
>
<StackPanel Background="PowderBlue">
<Glyphs
FontUri = "C:\WINDOWS\Fonts\TIMES.TTF"
FontRenderingEmSize = "100"
StyleSimulations = "BoldSimulation"
UnicodeString = "Hello World!"
Fill = "Black"
OriginX = "100"
OriginY = "200"
/>
</StackPanel>
</Page>
使用 DrawGlyphRun
如果你有自定义控件并且想要呈现字形,请使用 DrawGlyphRun 方法。
WPF 还通过使用 FormattedText 对象提供自定义文本格式的较低级别服务。 在 Windows Presentation Foundation(WPF)中呈现文本的最高效方法是使用 Glyphs 和 GlyphRun在字形级别生成文本内容。 但是,这种效率的代价是失去了易于使用的富文本格式功能,这些功能是 Windows Presentation Foundation(WPF)控件的内置特性,如 TextBlock 和 FlowDocument。
格式文本对象
使用 FormattedText 对象可以绘制多行文本,其中文本中的每个字符都可以单独设置格式。 有关详细信息,请参阅绘制格式化文本。
若要创建格式化文本,请调用 FormattedText 构造函数来创建 FormattedText 对象。 创建初始格式化文本字符串后,可以应用一系列格式样式。 如果应用程序想要实现自己的布局,则 FormattedText 对象比使用控件(如 TextBlock)更好。 有关 FormattedText 对象的详细信息,请参阅 绘图格式化文本。
FormattedText 对象提供低级别文本格式设置功能。 可以将多个格式样式应用于一个或多个字符。 例如,可以同时调用 SetFontSize 和 SetForegroundBrush 方法来更改文本中前五个字符的格式。
下面的代码示例创建一个 FormattedText 对象并呈现它。
protected override void OnRender(DrawingContext drawingContext)
{
string testString = "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor";
// Create the initial formatted text string.
FormattedText formattedText = new FormattedText(
testString,
CultureInfo.GetCultureInfo("en-us"),
FlowDirection.LeftToRight,
new Typeface("Verdana"),
32,
Brushes.Black);
// Set a maximum width and height. If the text overflows these values, an ellipsis "..." appears.
formattedText.MaxTextWidth = 300;
formattedText.MaxTextHeight = 240;
// Use a larger font size beginning at the first (zero-based) character and continuing for 5 characters.
// The font size is calculated in terms of points -- not as device-independent pixels.
formattedText.SetFontSize(36 * (96.0 / 72.0), 0, 5);
// Use a Bold font weight beginning at the 6th character and continuing for 11 characters.
formattedText.SetFontWeight(FontWeights.Bold, 6, 11);
// Use a linear gradient brush beginning at the 6th character and continuing for 11 characters.
formattedText.SetForegroundBrush(
new LinearGradientBrush(
Colors.Orange,
Colors.Teal,
90.0),
6, 11);
// Use an Italic font style beginning at the 28th character and continuing for 28 characters.
formattedText.SetFontStyle(FontStyles.Italic, 28, 28);
// Draw the formatted text string to the DrawingContext of the control.
drawingContext.DrawText(formattedText, new Point(10, 0));
}
Protected Overrides Sub OnRender(ByVal drawingContext As DrawingContext)
Dim testString As String = "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor"
' Create the initial formatted text string.
Dim formattedText As New FormattedText(testString, CultureInfo.GetCultureInfo("en-us"), FlowDirection.LeftToRight, New Typeface("Verdana"), 32, Brushes.Black)
' Set a maximum width and height. If the text overflows these values, an ellipsis "..." appears.
formattedText.MaxTextWidth = 300
formattedText.MaxTextHeight = 240
' Use a larger font size beginning at the first (zero-based) character and continuing for 5 characters.
' The font size is calculated in terms of points -- not as device-independent pixels.
formattedText.SetFontSize(36 * (96.0 / 72.0), 0, 5)
' Use a Bold font weight beginning at the 6th character and continuing for 11 characters.
formattedText.SetFontWeight(FontWeights.Bold, 6, 11)
' Use a linear gradient brush beginning at the 6th character and continuing for 11 characters.
formattedText.SetForegroundBrush(New LinearGradientBrush(Colors.Orange, Colors.Teal, 90.0), 6, 11)
' Use an Italic font style beginning at the 28th character and continuing for 28 characters.
formattedText.SetFontStyle(FontStyles.Italic, 28, 28)
' Draw the formatted text string to the DrawingContext of the control.
drawingContext.DrawText(formattedText, New Point(10, 0))
End Sub
FlowDocument、TextBlock 和标签控件
WPF 包含多个控件,用于将文本绘制到屏幕。 每个控件都针对不同的方案,并有自己的功能和限制列表。
FlowDocument 对性能的影响比 TextBlock 或 Label 更大
通常,当需要有限的文本支持时,应使用 TextBlock 元素,例如用户界面(UI)中的简短句子。 当需要最少的文本支持时,可以使用 Label。 FlowDocument 元素是支持丰富内容呈现的可重新流动文档的容器,因此,与使用 TextBlock 或 Label 控件相比,性能影响更大。
有关 FlowDocument的详细信息,请参阅 流文档概述。
避免在 FlowDocument 中使用 TextBlock
TextBlock 元素派生自 UIElement。 Run 元素派生自 TextElement,其使用成本低于 UIElement派生对象的成本。 如果可能,请使用 Run 而不是 TextBlock 在 FlowDocument中显示文本内容。
以下标记示例演示了在 FlowDocument中设置文本内容的两种方法:
<FlowDocument>
<!-- Text content within a Run (more efficient). -->
<Paragraph>
<Run>Line one</Run>
</Paragraph>
<!-- Text content within a TextBlock (less efficient). -->
<Paragraph>
<TextBlock>Line two</TextBlock>
</Paragraph>
</FlowDocument>
避免使用“Run”命令来设置文本属性
一般情况下,在 Run 中使用 TextBlock 对性能要求更高,而不是完全不使用显式 Run 对象。 如果使用 Run 来设置文本属性,请直接在 TextBlock 上设置这些属性。
以下标记示例演示了设置文本属性的两种方法,在本例中为 FontWeight 属性:
<!-- Run is used to set text properties. -->
<TextBlock>
<Run FontWeight="Bold">Hello, world</Run>
</TextBlock>
<!-- TextBlock is used to set text properties, which is more efficient. -->
<TextBlock FontWeight="Bold">
Hello, world
</TextBlock>
下表显示了在有和没有显式 TextBlock的情况下显示 1000 个 Run 对象的成本。
TextBlock 类型 | 创建时间(ms) | 呈现时间(ms) |
---|---|---|
运行设置文本属性 | 146 | 540 |
TextBlock 设置文本属性 | 43 | 453 |
避免将数据绑定到 Label.Content 属性
假设有一个 Label 对象,该对象经常从 String 源更新。 将数据 Label 元素的 Content 属性绑定到 String 源对象时,可能会遇到性能不佳的问题。 每次更新源 String 时,都会丢弃旧 String 对象并重新创建一个新的 String,因为 String 对象是不可变的,因此无法修改它。 而这又会导致 ContentPresenter 对象的 Label 丢弃其旧的内容,并重新生成新的内容,以显示新的 String。
此问题的解决方案很简单。 如果未将 Label 设置为自定义 ContentTemplate 值,请将 Label 替换为 TextBlock 和数据将其 Text 属性绑定到源字符串。
数据绑定属性 | 更新时间(ms) |
---|---|
Label.Content | 835 |
文本块.Text | 242 |
超链接
Hyperlink 对象是一个内联级流内容元素,可用于在流内容中托管超链接。
在一个 TextBlock 对象中合并超链接
可以通过将多个 Hyperlink 元素组合在同一 TextBlock中来优化其使用。 这有助于最大程度地减少在应用程序中创建的对象数。 例如,你可能想要显示多个超链接,例如:
MSN 主页 |我的 MSN
以下标记示例显示了用于显示超链接的多个 TextBlock 元素:
<!-- Hyperlinks in separate TextBlocks. -->
<TextBlock>
<Hyperlink TextDecorations="None" NavigateUri="http://www.msn.com">MSN Home</Hyperlink>
</TextBlock>
<TextBlock Text=" | "/>
<TextBlock>
<Hyperlink TextDecorations="None" NavigateUri="http://my.msn.com">My MSN</Hyperlink>
</TextBlock>
以下标记示例演示了一种更有效的超链接显示方式,这次使用单个 TextBlock:
<!-- Hyperlinks combined in the same TextBlock. -->
<TextBlock>
<Hyperlink TextDecorations="None" NavigateUri="http://www.msn.com">MSN Home</Hyperlink>
<Run Text=" | " />
<Hyperlink TextDecorations="None" NavigateUri="http://my.msn.com">My MSN</Hyperlink>
</TextBlock>
仅在 MouseEnter 事件的超链接中显示下划线
TextDecoration 对象是可以添加到文本中的视觉装饰;但是会占用实例化的资源。 如果广泛使用 Hyperlink 元素,请考虑仅在触发事件(如 MouseEnter 事件)时显示下划线。 有关详细信息,请参阅 指定超链接是否带有下划线。
下图显示了 MouseEnter 事件如何触发带下划线的超链接:
以下标记示例演示使用和不使用下划线定义的 Hyperlink:
<!-- Hyperlink with default underline. -->
<Hyperlink NavigateUri="http://www.msn.com">
MSN Home
</Hyperlink>
<Run Text=" | " />
<!-- Hyperlink with no underline. -->
<Hyperlink Name="myHyperlink" TextDecorations="None"
MouseEnter="OnMouseEnter"
MouseLeave="OnMouseLeave"
NavigateUri="http://www.msn.com">
My MSN
</Hyperlink>
下表显示了使用和不使用下划线显示 1000 个 Hyperlink 元素的性能成本。
超链接 | 创建时间(ms) | 呈现时间(ms) |
---|---|---|
使用下划线 | 289 | 1130 |
不使用下划线 | 299 | 776 |
文本格式设置功能
WPF 提供丰富的文本格式服务,例如自动断字。 这些服务可能会影响应用程序性能,仅在需要时才应使用。
避免不必要地使用断字
自动断字功能查找文本行的断字符位置,并允许在 TextBlock 和 FlowDocument 对象中为行设置额外的断点。 默认情况下,这些对象中禁用自动断字符功能。 可以通过将对象的 IsHyphenationEnabled 属性设置为 true
来启用此功能。 但是,启用此功能会导致 WPF 启动组件对象模型(COM)互操作性,这可能会影响应用程序性能。 建议不要使用自动连字符,除非需要。
谨慎使用图表
Figure 元素代表在某页内容中绝对可定位的一部分流内容。 在某些情况下,如果 Figure 的位置与已布局的内容相冲突,则整个页面可能会自动重新格式化。可以通过将 Figure 元素组合在一起,或在固定页面大小方案中将元素声明在内容顶部,从而最大程度地减少不必要的重新格式化的可能性。
最佳段落
FlowDocument 对象的最佳段落功能布局段落,以便尽可能均匀地分布空格。 默认情况下,禁用最佳段落功能。 可以通过将对象的 IsOptimalParagraphEnabled 属性设置为 true
来启用此功能。 但是,启用此功能会影响应用程序性能。 建议不要使用最佳段落功能,除非需要它。