Flow Document Overview
Flow documents are designed to optimize viewing and readability. Rather than being set to one predefined layout, flow documents dynamically adjust and reflow their content based on run-time variables such as window size, device resolution, and optional user preferences. In addition, flow documents offer advanced document features, such as pagination and columns. This topic provides an overview of flow documents and how to create them.
A flow document is designed to "reflow content" depending on window size, device resolution, and other environment variables. In addition, flow documents have a number of built in features including search, viewing modes that optimize readability, and the ability to change the size and appearance of fonts. Flow Documents are best utilized when ease of reading is the primary document consumption scenario. In contrast, Fixed Documents are designed to have a static presentation. Fixed Documents are useful when fidelity of the source content is essential. See Documents in WPF for more information on different types of documents.
The following illustration shows a sample flow document viewed in several windows of different sizes. As the display area changes, the content reflows to make the best use of the available space.
As seen in the image above, flow content can include many components including paragraphs, lists, images, and more. These components correspond to elements in markup and objects in procedural code. We will go over these classes in detail later in the Flow Related Classes section of this overview. For now, here is a simple code example that creates a flow document consisting of a paragraph with some bold text and a list.
The illustration below shows what this code snippet looks like.
In this example, the FlowDocumentReader control is used to host the flow content. See Flow Document Types for more information on flow content hosting controls. Paragraph, List, ListItem, and Bold elements are used to control content formatting, based on their order in markup. For example, the Bold element spans across only part of the text in the paragraph; as a result, only that part of the text is bold. If you have used HTML, this will be familiar to you.
As highlighted in the illustration above, there are several features built into Flow Documents:
Search: Allows the user to perform a full text search of an entire document.
Viewing Mode: The user can select their preferred viewing mode including a single-page (page-at-a-time) viewing mode, a two-page-at-a-time (book reading format) viewing mode, and a continuous scrolling (bottomless) viewing mode. For more information about these viewing modes, see FlowDocumentReaderViewingMode.
Page Navigation Controls: If the viewing mode of the document uses pages, the page navigation controls include a button to jump to the next page (the down arrow) or previous page (the up arrow), as well as indicators for the current page number and total number of pages. Flipping through pages can also be accomplished using the keyboard arrow keys.
Zoom: The zoom controls enable the user to increase or decrease the zoom level by clicking the plus or minus buttons, respectively. The zoom controls also include a slider for adjusting the zoom level. For more information, see Zoom.
These features can be modified based upon the control used to host the flow content. The next section describes the different controls.
Display of flow document content and how it appears is dependent upon what object is used to host the flow content. There are four controls that support viewing of flow content: FlowDocumentReader, FlowDocumentPageViewer, RichTextBox, and FlowDocumentScrollViewer. These controls are briefly described below.
FlowDocumentReader includes features that enable the user to dynamically choose between various viewing modes, including a single-page (page-at-a-time) viewing mode, a two-page-at-a-time (book reading format) viewing mode, and a continuous scrolling (bottomless) viewing mode. For more information about these viewing modes, see FlowDocumentReaderViewingMode. If you do not need the ability to dynamically switch between different viewing modes, FlowDocumentPageViewer and FlowDocumentScrollViewer provide lighter-weight flow content viewers that are fixed in a particular viewing mode.
FlowDocumentPageViewer shows content in page-at-a-time viewing mode, while FlowDocumentScrollViewer shows content in continuous scrolling mode. Both FlowDocumentPageViewer and FlowDocumentScrollViewer are fixed to a particular viewing mode. Compare to FlowDocumentReader, which includes features that enable the user to dynamically choose between various viewing modes (as provided by the FlowDocumentReaderViewingMode enumeration), at the cost of being more resource intensive than FlowDocumentPageViewer or FlowDocumentScrollViewer.
By default, a vertical scrollbar is always shown, and a horizontal scrollbar becomes visible if needed. The default UI for FlowDocumentScrollViewer does not include a toolbar; however, the IsToolBarVisible property can be used to enable a built-in toolbar.
You use a RichTextBox when you want to allow the user to edit flow content. For example, if you wanted to create an editor that allowed a user to manipulate things like tables, italic and bold formatting, etc, you would use a RichTextBox. See RichTextBox Overview for more information.
Note: Flow content inside a RichTextBox does not behave exactly like flow content contained in other controls. For example, there are no columns in a RichTextBox and hence no automatic resizing behavior. Also, the typically built in features of flow content like search, viewing mode, page navigation, and zoom are not available within a RichTextBox.
Flow content can be complex, consisting of various elements including text, images, tables, and even UIElement derived classes like controls. To understand how to create complex flow content, the following points are critical:
Flow-related Classes: Each class used in flow content has a specific purpose. In addition, the hierarchical relation between flow classes helps you understand how they are used. For example, classes derived from the Block class are used to contain other objects while classes derived from Inline contain objects that are displayed.
Content Schema: A flow document can require a substantial number of nested elements. The content schema specifies possible parent/child relationships between elements.
The following sections will go over each of these areas in more detail.
The diagram below shows the objects most typically used with flow content:
For the purposes of flow content, there are two important categories:
Block-derived classes: Also called "Block content elements" or just "Block Elements". Elements that inherit from Block can be used to group elements under a common parent or to apply common attributes to a group.
Inline-derived classes: Also called "Inline content elements" or just "Inline Elements". Elements that inherit from Inline are either contained within a Block Element or another Inline Element. Inline Elements are often used as the direct container of content that is rendered to the screen. For example, a Paragraph (Block Element) can contain a Run (Inline Element) but the Run actually contains the text that is rendered on the screen.
Each class in these two categories is briefly described below.
Paragraph is typically used to group content into a paragraph. The simplest and most common use of Paragraph is to create a paragraph of text.
However, you can also contain other inline-derived elements as you will see below.
Section is used only to contain other Block-derived elements. It does not apply any default formatting to the elements it contains. However, any property values set on a Section applies to its child elements. A section also enables you to programmatically iterate through its child collection. Section is used in a similar manner to the <DIV> tag in HTML.
BlockUIContainer enables UIElement elements (i.e. a Button) to be embedded in block-derived flow content. InlineUIContainer (see below) is used to embed UIElement elements in inline-derived flow content. BlockUIContainer and InlineUIContainer are important because there is no other way to use a UIElement in flow content unless it is contained within one of these two elements.
<FlowDocument ColumnWidth="400"> <Section Background="GhostWhite"> <Paragraph> A UIElement element may be embedded directly in flow content by enclosing it in a BlockUIContainer element. </Paragraph> <BlockUIContainer> <Button>Click me!</Button> </BlockUIContainer> <Paragraph> The BlockUIContainer element may host no more than one top-level UIElement. However, other UIElements may be nested within the UIElement contained by an BlockUIContainer element. For example, a StackPanel can be used to host multiple UIElement elements within a BlockUIContainer element. </Paragraph> <BlockUIContainer> <StackPanel> <Label Foreground="Blue">Choose a value:</Label> <ComboBox> <ComboBoxItem IsSelected="True">a</ComboBoxItem> <ComboBoxItem>b</ComboBoxItem> <ComboBoxItem>c</ComboBoxItem> </ComboBox> <Label Foreground ="Red">Choose a value:</Label> <StackPanel> <RadioButton>x</RadioButton> <RadioButton>y</RadioButton> <RadioButton>z</RadioButton> </StackPanel> <Label>Enter a value:</Label> <TextBox> A text editor embedded in flow content. </TextBox> </StackPanel> </BlockUIContainer> </Section> </FlowDocument>
The following figure shows how this example renders.
List is used to create a bulleted or numeric list. Set the MarkerStyle property to a TextMarkerStyle enumeration value to determine the style of the list. The example below shows how to create a simple list.
Table is used to create a table. Table is similar to the Grid element but it has more capabilities and, therefore, requires greater resource overhead. Because Grid is a UIElement, it cannot be used in flow content unless it is contained in a BlockUIContainer or InlineUIContainer. For more information on Table, see Table Overview.
Run is used to contain unformatted text. You might expect Run objects to be used extensively in flow content. However, in markup, Run elements are not required to be used explicitly. Run is required to be used when creating or manipulating flow documents by using code. For example, in the markup below, the first Paragraph specifies the Run element explicitly while the second does not. Both paragraphs generate identical output.
Note: Starting in the .NET Framework 4, the Text property of the Run object is a dependency property. You can bind the Text property to a data source, such as a TextBlock. The Text property fully supports one-way binding. The Text property also supports two-way binding, except for RichTextBox. For an example, see Run.Text.
Span groups other inline content elements together. No inherent rendering is applied to content within a Span element. However, elements that inherit from Span including Hyperlink, Bold, Italic and Underline do apply formatting to text.
The following screenshot shows how this example renders.
InlineUIContainer enables UIElement elements (i.e. a control like Button) to be embedded in an Inline content element. This element is the inline equivalent to BlockUIContainer described above. Below is an example that uses InlineUIContainer to insert a Button inline in a Paragraph.
Figure and Floater
Figure and Floater are used to embed content in Flow Documents with placement properties that can be customized independent of the primary content flow. Figure or Floater elements are often used to highlight or accentuate portions of content, to host supporting images or other content within the main content flow, or to inject loosely related content such as advertisements.
The following example shows how to embed a Figure into a paragraph of text.
The following illustration shows how this example renders.
Can be positioned: You can set its horizontal and vertical anchors to dock it relative to the page, content, column or paragraph. You can also use its HorizontalOffset and VerticalOffset properties to specify arbitrary offsets.
Is sizable to more than one column: You can set Figure height and width to multiples of page, content or column height or width. Note that in the case of page and content, multiples greater than 1 are not allowed. For example, you can set the width of a Figure to be "0.5 page" or "0.25 content" or "2 Column". You can also set height and width to absolute pixel values.
Cannot be positioned and will render wherever space can be made available for it. You cannot set the offset or anchor a Floater.
Cannot be sized to more than one column: By default, Floater sizes at one column. It has a Width property that can be set to an absolute pixel value, but if this value is greater than one column width it is ignored and the floater is sized at one column. You can size it to less than one column by setting the correct pixel width, but sizing is not column-relative, so "0.5Column" is not a valid expression for Floater width. Floater has no height property and it's height cannot be set, it’s height depends on the content
Floater paginates: If its content at its specified width extends to more than 1 column height, floater breaks and paginates to the next column, the next page, etc.
Figure is a good place to put standalone content where you want to control the size and positioning, and are confident that the content will fit in the specified size. Floater is a good place to put more free-flowing content that flows similar to the main page content, but is separated from it.
The following screenshot shows how this example renders.
myParagraph.Inlines.Add(new Run("Some text"));
Section secx = new Section(); secx.Blocks.Add(new Paragraph(new Run("A bit of text content...")));
When working with flow content programmatically, you will likely make extensive use of these collections.
Whether a flow element uses an InlineCollection (Inlines) or BlockCollection (Blocks) to contain its child elements depends on what type of child elements (Block or Inline) can be contained by the parent. Containment rules for flow content elements are summarized in the content schema in the next section.
Note: There is a third type of collection used with flow content, the ListItemCollection, but this collection is only used with a List. In addition, there are several collections used with Table. See Table Overview for more information.
Given the number of different flow content elements, it can be overwhelming to keep track of what type of child elements an element can contain. The diagram below summarizes the containment rules for flow elements. The arrows represent the possible parent/child relationships.
As can be seen from the diagram above, the children allowed for an element are not necessarily determined by whether it is a Block element or an Inline element. For example, a Span (an Inline element) can only have Inline child elements while a Figure (also an Inline element) can only have Block child elements. Therefore, a diagram is useful for quickly determining what element can be contained in another. As an example, let's use the diagram to determine how to construct the flow content of a RichTextBox.
Thus far, this is what the markup might look like.
2. According to the diagram, there are several Block elements to choose from including Paragraph, Section, Table, List, and BlockUIContainer (see Block-derived classes above). Let's say we want a Table. According to the diagram above, a Table contains a TableRowGroup containing TableRow elements, which contain TableCell elements which contain a Block-derived object. Below is the corresponding segment for Table taken from the diagram above.
Below is the corresponding markup.
3. Again, one or more Block elements are required underneath a TableCell. To make it simple, let's place some text inside the cell. We can do this using a Paragraph with a Run element. Below is the corresponding segments from the diagram showing that a Paragraph can take an Inline element and that a Run (an Inline element) can only take plain text.
Below is the entire example in markup.
Usually text is the most prevalent type of content in a flow document. Although the objects introduced above can be used to control most aspects of how text is rendered, there are some other methods for customizing text that is covered in this section.
Text decorations allow you to apply the underline, overline, baseline, and strikethrough effects to text (see pictures below). These decorations are added using the TextDecorations property that is exposed by a number of objects including Inline, Paragraph, TextBlock, and TextBox.
<FlowDocument ColumnWidth="200"> <Paragraph TextDecorations="Strikethrough"> This text will render with the strikethrough effect. </Paragraph> </FlowDocument>
Paragraph parx = new Paragraph(new Run("This text will render with the strikethrough effect.")); parx.TextDecorations = TextDecorations.Strikethrough;
The following figure shows how this example renders.
The following figures show how the Overline, Baseline, and Underline decorations render, respectively.
The Typography property is exposed by most flow-related content including TextElement, FlowDocument, TextBlock, and TextBox. This property is used to control typographical characteristics/variations of text (i.e. small or large caps, making superscripts and subscripts, etc).
<Paragraph TextAlignment="Left" FontSize="18" FontFamily="Palatino Linotype" Typography.NumeralStyle="OldStyle" Typography.Fraction="Stacked" Typography.Variants="Inferior" > <Run> This text has some altered typography characteristics. Note that use of an open type font is necessary for most typographic properties to be effective. </Run> <LineBreak/><LineBreak/> <Run> 0123456789 10 11 12 13 </Run> <LineBreak/><LineBreak/> <Run> 1/2 2/3 3/4 </Run> </Paragraph>
The following figure shows how this example renders.
In contrast, the following figure shows how a similar example with default typographic properties renders.
The following example shows how to set the Typography property programmatically.
Paragraph par = new Paragraph(); Run runText = new Run( "This text has some altered typography characteristics. Note" + "that use of an open type font is necessary for most typographic" + "properties to be effective."); Run runNumerals = new Run("0123456789 10 11 12 13"); Run runFractions = new Run("1/2 2/3 3/4"); par.Inlines.Add(runText); par.Inlines.Add(new LineBreak()); par.Inlines.Add(new LineBreak()); par.Inlines.Add(runNumerals); par.Inlines.Add(new LineBreak()); par.Inlines.Add(new LineBreak()); par.Inlines.Add(runFractions); par.TextAlignment = TextAlignment.Left; par.FontSize = 18; par.FontFamily = new FontFamily("Palatino Linotype"); par.Typography.NumeralStyle = FontNumeralStyle.OldStyle; par.Typography.Fraction = FontFraction.Stacked; par.Typography.Variants = FontVariants.Inferior;
See Typography in WPF for more information on typography.