다음을 통해 공유


WPF

Flexible Content Display With Flow Documents

Markus Egger

This article discusses:
  • Windows Presentation Foundation
  • Flow Documents
  • Flow Document rendering
  • Flow Document editing
This article uses the following technologies:
.NET Framework 3.0

Contents

Flow Document Basics
Viewing Flow Documents
Displaying Flow Documents
Creating Flow Documents
Exploring Layout Possibilities
Above and Beyond

Windows® Presentation Foundation (WPF) provides a great set of features. So many features, in fact, that even some very significant ones don’t get nearly the attention they deserve. A perfect example is the Flow Documents feature, which allows developers to create documents natively in WPF. In "XPS Documents: A First Look at APIs For Creating XML Paper Specification Documents" in the January 2006 issue of MSDN® Magazine, Bob Watson took a close look at XPS Documents in WPF, but Flow Documents are different. XPS (the XML Paper Specification) is geared towards printing and page-oriented content, while Flow Documents are aimed at screen reading and provide a more dynamic and arguably more sophisticated model. Flow Documents work for almost everything related to text content, from product descriptions to entire books.

The representation of text is undoubtedly one of the more important UI features. In WPF interfaces, you often use controls such as Label to display text. In many scenarios, however, you need more than a simple display of a few words. Flow Documents provide a more sophisticated approach, though they are fundamentally very simple. They define text flows in a fashion reminiscent of HTML documents, but they are more capable and provide significantly more advanced layout options.

Flow Documents are typically defined using Extensible Application Markup Language (XAML), the XML-based standard markup language. XAML is particularly intuitive for Flow Documents, mainly because of the similarity to HTML. The following Flow Document sample creates a single paragraph of text with simple bold formatting applied to a few words:

<FlowDocument
  xmlns=’https://schemas.microsoft.com/winfx/2006/xaml/presentation’
  xmlns:x=’https://schemas.microsoft.com/winfx/2006/xaml’>
  <Paragraph>The quick <Bold>brown fox</Bold> jumps over the lazy dog.
</Paragraph>
</FlowDocument>

As you can see, the similarities to HTML are even more apparent than in other XAML UIs. The actual element names are different, but at least for simple documents, the paradigm is very similar. Flow Documents generally start with a FlowDocument root element that contains a number of blocks. Blocks are elements within the flow, typically paragraphs of text as in the example above (although there are other block types as well). Paragraphs can in turn contain other elements, such as the two bolded words in this example. Note that as with any other XAML document, the root element must have the XAML-specific namespace definitions in order to be recognized. This is an implementation detail specific to XAML and has nothing to do with Flow Documents. Note that the namespace definition is only necessary in standalone Flow Documents. (Flow Documents can be part of a larger XAML UI, in which case the UI’s root element gets the namespace definitions.)

Of course, users never see the XAML for Flow Documents (unlike HTML source that can be viewed in the browser), just as they don’t see the XAML for any other UI element. Instead, users see the final rendering of the document. For this particular example, you can see the result in a number of ways. Perhaps the simplest is to type it into XamlPad, a utility that ships with the Windows SDK (see Figure 1).

Figure 1 Very Simple Flow Document Displayed in XamlPad

Figure 1** Very Simple Flow Document Displayed in XamlPad **(Click the image for a larger view)

Of course, this is a very simple example; the definition of a document and the embedded layout can be much more sophisticated. Flow Documents support all the formatting you’d expect, such as italic, underline, font colors and typefaces, and much more. Figure 2 shows a slightly more advanced example, the result of which can be seen in Figure 3.

Figure 2 More Formatting and a Bulleted List

<FlowDocument 
  xmlns=’https://schemas.microsoft.com/winfx/2006/xaml/presentation’
  xmlns:x=’https://schemas.microsoft.com/winfx/2006/xaml’>
  <Paragraph FontFamily=”Calibri” FontWeight=”Bold” FontSize=”24”>
    WPF Flow Documents</Paragraph>
  <Paragraph>WPF Flow Documents are <Italic>quite sophisticated</Italic>. 
    They support all common layout options, as well as many you probably
    do <Span Foreground=”Red”>not expect</Span>.</Paragraph>
  <List>
    <ListItem><Paragraph>First List Item</Paragraph></ListItem>
    <ListItem><Paragraph>Second List Item</Paragraph></ListItem>
    <ListItem><Paragraph>Third List Item</Paragraph></ListItem>
  </List>
  <Paragraph>Of course, Flow Documents also support the definition 
    of <Bold><Span FontFamily=”Comic Sans MS” FontSize=”24”
    Foreground=”Blue”>in line</Span></Bold> font sizes and font faces.
  </Paragraph>
</FlowDocument>

Figure 3 Flow Document with Slightly More Advanced Formatting

Figure 3** Flow Document with Slightly More Advanced Formatting **(Click the image for a larger view)

This example shows several paragraphs with inline formatting. It also provides a first example of another type of block, namely a List, which, not surprisingly, contains a number of list items. Note that each list item in turn is also just a container for more blocks. So rather than simply putting text inside of a list item, I added a paragraph element to each. I could have added multiple paragraphs to each item or any other block type for that matter. This lets you create advanced layouts within a single bullet point of a list, something that is generally a problem in formats such as HTML, which only flow simple text strings into each list element.

Flow Document Basics

Now that you’ve seen some basic Flow Documents, let’s take a step back and look at some of the fundamentals again. As you’ve already seen, Flow Documents are collections of blocks. Internally, all blocks are WPF classes that derive from the System.Windows.Documents.Block class. Block in turn derives (a few steps further up the chain) from ContentElement, which is a fairly low-level class in WPF specifically optimized for document definition. This approach is somewhat similar to the controls you use to define WPF interfaces, which all derive from UIElement. Both inheritance trees are similar in concept, but not exactly the same. This means that WPF controls and blocks cannot be combined directly. For example, a button’s caption can’t be set to a paragraph of text, nor can a paragraph directly contain a button. There are some subtle differences in these controls and blocks, which result from the fact that layout within a content control and layout within a block work quite a bit differently. Luckily, the gap that needs to be bridged between the two types of WPF elements is rather narrow. Buttons, for instance, can contain TextBlock objects, which are made up of formatted text, and blocks can contain any WPF control by means of the special BlockUIContainer block class. This means that Flow Documents can contain all kinds of WPF elements (including interactive user interfaces, media, and 3D elements), and, looking at it from the other angle, Flow Documents can become part of any WPF user interface either as an advanced layout element for control contents or as a true document, such as the description of an item in a point of sale application.

The list of available blocks is theoretically open-ended, since developers can derive their own block classes and thus create their own enhancements to the document-rendering engine. This provides freedom that is not available in any other document-rendering engines that I am aware of. However, the number of blocks the average document creator is exposed to is typically limited. A list of the most important types of blocks is shown in Figure 4.

Figure 4 Important Block Types

Block Description
Paragraph Contains (potentially richly formatted) text.
List Contains lists of various kinds (numbered, bulleted, and so on).
Table Contains tables similar to those in Microsoft Word or HTML.
BlockUIContainer Contains various UI elements that are made part of the overall flow.
Section Contains a group of other blocks. Sections are handy for applying common attributes to a group of blocks, such as the same font attributes to multiple paragraphs.

When creating WPF Flow Documents in XAML, you really just instantiate certain types. Consider the following XAML snippet (from here on, I’ll omit the namespace definition to keep the examples simple):

<FlowDocument>
  <Paragraph>Hello World!</Paragraph>
</FlowDocument>

This instantiates a FlowDocument class and a Paragraph class (which has its text set to "Hello World!"). The paragraph is added to the FlowDocument’s blocks collection. Note that as with all XAML, the element names are case-sensitive and map exactly to the classes that are available as part of the WPF. You could also create the same document programmatically, like so:

FlowDocument doc = new FlowDocument();
Paragraph para = new Paragraph();
para.Inlines.Add(“Hello World!”);
doc.Blocks.Add(para);

Of course, this is much less intuitive than the declarative approach XAML provides, so the programmatic approach is only taken in special scenarios. (I sometimes use this approach when I need to create a richly formatted report that is more of a real document, rather than the tabular output created by many reporting engines.)

In many scenarios, paragraphs have richly formatted content themselves, which is also done by instantiating classes, like this:

<Paragraph>Hello <Bold>World!</Bold></Paragraph>

Here, the paragraph contains two segments of text—"Hello" (which uses the default format) and "World!" (which is bold). This is interesting because it means that this XAML doesn’t just instantiate a paragraph and set its text as a simple string; instead it creates a paragraph with two child segments, each of which contains text with different formatting. In WPF, these segments are called inlines. Just like a Flow Document can contain multiple blocks of various types, paragraphs can contain inlines of various types. Inlines come in a number of variations. Some inlines are so-called Spans, which represent a segment of text with certain formatting options applied. The Bold element used in this example is a special case of a Span, with the default font weight set to bold. Another type of an inline is a Run, which is a segment of text with default formatting. Therefore, the XAML above is really just shorthand for this:

<Paragraph>
  <Run>Hello </Run>
  <Bold>World!</Bold>
</Paragraph>

Of course, it is much more convenient that you do not have to define each inline in XAML, but if you were to create the same example programmatically, it is important to understand the concept of inlines, since they cannot be omitted in code. Here is the programmatic equivalent of the previous two XAML examples:

Paragraph para = new Paragraph();
para.Inlines.Add(new Run(“Hello “));
Bold b = new Bold();
b.Inlines.Add(“World!”);
para.Inlines.Add(b);

Bold is a special version of a Span, with its default font weight set to bold; the Bold type is subclassed from Span and overrides the FontWeight property. There are similar special Spans, such as Italic and Underline. However, these special Spans are not strictly needed, because you could also use a default Span and set the appropriate properties instead:

<Paragraph>Hello <Span FontWeight=”Bold”>World!</Span></Paragraph>

Of course, the ability to directly specify attributes such as bold and italic by wrapping a certain section of text into Bold and Italic tags is very convenient and intuitive, so it is much more common to use <Bold> than <Span FontWeight="Bold">. Nevertheless, the <Span> element is very useful since there are numerous properties that can be set beyond simple font bolding, and most of those formatting options do not have individual Span types. In fact, for many very common formatting options, there is no special Span. A typical example is setting font faces. Unlike HTML, Flow Documents do not have <Font> elements. Instead, fonts are set like so:

<Paragraph>Hello <Span FontFamily=”Comic Sans MS” FontSize=”24”>
    World!</Span></Paragraph>

Many properties, such as FontFamily, can be found consistently on all Flow Document classes. For instance, if you wanted to set the font for a complete paragraph rather than just an inline, you can do it without a Span:

<Paragraph FontFamily=”Comic Sans MS” FontSize=”24”>Hello World!</Paragraph>

There are also inlines other than Spans and Runs. Here some of the other more interesting inlines:

Figure Figures are somewhat unusual inlines, because they contain blocks. Thus, in a way, Figures are almost like mini Flow Documents within Flow Documents. Figures are often used for advanced layout features, such as images within paragraphs where the normal text flows around the image.

Floater Floaters are lightweight figures. They do not support all the figure positioning options, but if all you need is the ability to do simple alignment outside the standard paragraph alignment, floaters may be useful.

LineBreak LineBreak elements do exactly what their name suggests: they introduce line breaks within paragraphs.

InlineUIContainer The InlineUIContainer is the inline equivalent of the BlockUIContainer. If you need to combine any sort of WPF control with your other inlines (such as having a button flow inside a paragraph’s text), the InlineUIContainer is what you need.

Figures are used all the time in Flow Documents (as are LineBreaks, but they hardly require a detailed discussion). The following example uses a figure to display an image as part of a larger flow:

<Paragraph>
  <Figure Width=”200”>
    <BlockUIContainer>
      <Image Source=”Pictures\Humpback Whale.jpg” />
    </BlockUIContainer>
    <Paragraph Foreground=”Blue” FontFamily=”Consolas”>
        The Whale</Paragraph>
  </Figure>
  The quick brown fox jumps over the lazy dog. The quick brown...
</Paragraph>

Note that there is no Image block in WPF Flow Documents. Instead, images are embedded as BlockUIContainers with standard WPF Image controls. (The same approach is used for content such as videos or interactive 3D models inside of Flow Content). Figure 5 shows the rendering of a document similar to this one.

Figure 5 Text Flows around Image and Caption

Figure 5** Text Flows around Image and Caption **(Click the image for a larger view)

Viewing Flow Documents

You have now seen how to create some simple Flow Documents and view them in XamlPad. What I’ve ignored so far is how you would view Flow Documents in the wild. After all, you wouldn’t expect your users to bring out XamlPad and paste the document’s XAML. One way of viewing a XAML Flow Document is to save it as a file with a .xaml extension and double-click it in Windows Explorer. This launches the default application associated with XAML files (typically Internet Explorer®), which shows the document. Figure 6 shows the result.

Figure 6 XAML Flow Document in Internet Explorer

Figure 6** XAML Flow Document in Internet Explorer **(Click the image for a larger view)

The fact that Internet Explorer (and other browsers) can display XAML content is of particular interest, since this is your ticket to displaying Flow Documents as part of your Web applications. In other words, if you upload XAML Flow Documents to your Web server and someone browses to the file, he will see something similar to Figure 6 (assuming the user has the Microsoft® .NET Framework 3.0 installed). Of course, this also works dynamically. If your ASP.NET Web application (or any other server-side technology) generates a XAML Flow Document on the fly and returns it as its output (and assuming the content type is set appropriately to "application/xaml+xml"), the user will see a Flow Document as part of your Web application, which of course is extremely useful in many scenarios. Figure 7 shows a simple ASP.NET page that generates a Flow Document.

Displaying Flow Documents

You may have noticed that whenever a Flow Document is displayed (either in the browser or in XamlPad), there seems to be a tad more in the display than just the document itself. In particular, there are controls rendered along the bottom of the document. As you can see in Figure 8, by default Flow Documents are rendered by means of a FlowDocumentReader control, which provides a set of standard features such as zoom, paging, various view modes, and even a find feature. As it turns out, Flow Documents need to be hosted by some sort of control that is capable of displaying them. The default viewer for Flow Documents is a FlowDocumentReader control, which is instantiated automatically unless you explicitly use a different control. WPF currently provides three different controls for viewing Flow Documents:

Figure 8 Control Buttons in the FlowDocumentReader Control

Figure 8** Control Buttons in the FlowDocumentReader Control **(Click the image for a larger view)

FlowDocumentScrollViewer This control displays documents in a continuous flow with a scrollbar, similar to Web pages or the Web Layout in Microsoft Word. Figure 9 shows a document in a scroll viewer.

Figure 9 Using the FlowDocumentScrollViewer Control

Figure 9** Using the FlowDocumentScrollViewer Control **(Click the image for a larger view)

FlowDocumentPageViewer This control displays Flow Documents in individual pages, with page flipping instead of scrolling. This is similar to the Full Screen Reading mode in Word. Figure 10 shows a page viewer. There the document from Figure 9 is rendered in a FlowDocumentPageViewer control and the scrollbar is replaced by a paging mechanism. The simple flow layout approach has been replaced with an advanced, multi-column, paged layout.

Figure 10 Using the FlowDocumentPageViewer Control

Figure 10** Using the FlowDocumentPageViewer Control **(Click the image for a larger view)

FlowDocumentReader This control combines the scroll viewer and the page viewer and allows the user to switch between the two approaches. This is the default control used for Flow Documents, and it is often a great choice for applications that feature sophisticated text display. In Figure 11, the same document shown in Figures 9 and 10 was rendered in a FlowDocumentReader, which combines the scroll viewer and page viewer approaches. In addition, it enables the search feature which is hidden by default in the other controls (the other viewers do support find, and it can be shown either by executing the ApplicationCommands.Find command or by using Ctrl+F from the keyboard). The reader control also supports a multi-page view, which slightly alters the page-based rendering and the way columns and figures are rendered.

Figure 11 Using the FlowDocumentReader Control

Figure 11** Using the FlowDocumentReader Control **(Click the image for a larger view)

The control you choose will depend on your situation, though the FlowDocumentReader is compelling for all but the most basic uses. It is versatile and powerful and it supports paged layouts, which is superior to scrolling in many scenarios. A detailed discussion of the topic is beyond the scope of this article, but it turns out that scrolling and associated effects, such as doubling, is one of the main reasons people prefer print over digital text. The paged approach is more natural in many situations and should help to make digital reading more widely accepted.

So how do you define which control to use? A simple—though rather brute-force—approach is to add the desired control to the document’s XAML:

<FlowDocumentScrollViewer 
  xmlns=’https://schemas.microsoft.com/winfx/2006/xaml/presentation’
  xmlns:x=’https://schemas.microsoft.com/winfx/2006/xaml’>
  <FlowDocument>
    <Paragraph>The quick <Bold>brown fox</Bold> jumps over the lazy
        dog.</Paragraph>
  </FlowDocument>
</FlowDocumentScrollViewer>

In this example, the document root has been made a FlowDocumentScrollViewer tag. This means you are not just defining a pure document anymore. Instead, you are defining a complete XAML interface, which happens to be using a scroll viewer as its root. The content of the scroll viewer is the Flow Document from the very first example. (Note that the namespace definitions are now with the scroll viewer tag, rather than the Flow Document tag). Figures 9 through 11 were created using this approach, with different viewer controls used as the root element

Why do I call this a brute force approach? Because, from an architectural point of view, it has some problems caused by mixing the user interface definition with its actual data. It is much more desirable to keep the document separate from its interface. Mixing the reader with the document is a little like creating a SQL Server™ table and somehow defining that it can only be displayed in a Windows Forms DataGrid. There are several ways of keeping the document separate from the UI definition. If you want to display Flow Documents as part of a Web application using the demonstrated ASP.NET approach, you can define the ASP.NET page with the desired viewer control and simply merge in the actual content (which is stored separately, perhaps in a database) using standard ASP.NET code.

In a typical WPF application, on the other hand, you can simply define your user interface using standard WPF, Windows, and XAML Browser Application (XBAP) approaches, and then load your document dynamically. Figure 12 shows a simple example that uses a fictitious library of my articles, which are displayed in a listbox in the top-left corner. When the user selects an article from the list, the document is loaded dynamically into the Flow Document Reader control that takes up most of the form. Note that standard WPF techniques, such as alpha blending, work in this setup. You’ll notice that the actual Flow Document is semi-transparent and the photo of me in the background shines through Also note that the app uses a listbox, imagery, a label, and a FlowDocumentReader control to create a library of fictitious articles.

Figure 12 Using Listbox, Image, Label, and FlowDocumentReader Controls

Figure 12** Using Listbox, Image, Label, and FlowDocumentReader Controls **(Click the image for a larger view)

The trickiest part of this example is loading the actual document into the viewer control. This is done by means of the System.Windows.Markup.XamlReader class, which allows for the dynamic loading of any XAML content, including, but not limited to, Flow Documents. Here is the line of code I wired up to the selection change event of the listbox:

documentReader.Document = 
    (FlowDocument)XamlReader.Load(
File.OpenRead(fileName));

The Load method returns an object, since the root element in the XAML file could represent many different types. In my example, I know that the return value is a FlowDocument, so I simply perform a cast and assign that document to the FlowDocumentReader control’s Document property (I named the control instance documentReader in this example). Keep in mind that this is just an example. Production-quality code definitely needs some error handling here as well.

Note that all the things you know about WPF apply to this example. For instance, the reader controls are just standard WPF controls that support styling. This means you can completely change the appearance of all UI elements such as the zoom bar, the view mode switches, or the paging controls. (The only element you have limited control over is the search box, though you do not have to use it at all if you don’t like it.)

Also, while my example shows a Windows-based application, the same application could be deployed as an XBAP and run inside a Web browser (again, of course, assuming the user has the .NET Framework 3.0 installed). Note that Microsoft Silverlight™ (formerly code-named "WPF/E") is not enough as Silverlight supports only a subset of WPF and does not support Flow Documents.

Creating Flow Documents

How are Flow Documents authored? Of course developers can always author Flow Documents using low-level tools such as XamlPad. However, in real-life settings, this is unlikely. Typically, Flow Documents are either created using WYSIWYG editors or by means of a content transformation from an existing document format. Since Flow Documents can be defined in XAML, it is particularly easy to convert existing XML content. However, it is also possible to convert HTML and Word documents with a reasonable amount of effort (although coding is required, as no out-of-the-box tools have emerged for this as of yet).

For WYSIWYG editing, WPF provides a ready-made control. The WPF RichTextBox control can edit XAML Flow Documents natively. The control’s name incorrectly suggests that it is made for Rich Text Format (RTF). Though it supports RTF, this control really operates mostly on FlowDocuments. In fact, the control really mirrors the Flow Document view controls, except that it supports editing. Some would even argue that the RichTextBox control should be considered another way of displaying Flow Documents.

Type the following example into XamlPad to see the RichTextBox control in action:

<RichTextBox
  xmlns=’https://schemas.microsoft.com/
      winfx/2006/xaml/presentation’
  xmlns:x=’https://schemas.microsoft.com/winfx/2006/xaml’>
  <FlowDocument>
    <Paragraph>
      The quick brown fox jumps over the lazy dog.
    </Paragraph>
  </FlowDocument>
</RichTextBox>

Just like the reader controls, the RichTextBox has a Document property, which you can automatically populate with a Flow Document in this session. This actually creates a UI that looks very similar to the FlowDocumentScrollViewer control, except that the text is editable. Note that this textbox control always handles Flow Documents in a scroll fashion. There is no way to edit Flow Documents in a RichTextBox in paged or multi-column mode. However, the result of the editing operation is a standard Flow Document that can be displayed in any of the viewer mechanisms you’ve seen already, including multi-column and paged modes.

One of the features of RichTextBox worth mentioning is the integrated spell checking. You can enable it like so:

<RichTextBox SpellCheck.IsEnabled=”true”>
  <FlowDocument>...</FlowDocument>
</RichTextBox>

Figure 13 shows the spell checker in action.

Figure 13 RichTextBox Control with Spell-Checking

Figure 13** RichTextBox Control with Spell-Checking **(Click the image for a larger view)

The only tricky part about using the control is loading and saving. In most scenarios, you probably won’t code the content of the RichTextBox into the UI XAML as in the earlier example. Instead, you will load and save the document dynamically. The load operation for text in a RichTextBox is identical to loading a Flow Document for a viewer control (see above). Saving a document is essentially the opposite: you take the document object and serialize it back to XAML, which can be done like so:

   System.Windows.Markup.XamlWriter.
   Save(richTextBox.Document)

This returns the XAML as a string, which you can then store to a file or a database or use any other way you can imagine.

The RichTextBox is very handy, but a word of caution is in order. While Flow Documents represent the most sophisticated technology available for rendering on-screen documents, the RichTextBox control is nowhere near as sophisticated. It is great for editing small documents and snippets of text, but you won’t be writing a book, magazine, or marketing brochure with it. For such long formats, it renders too simplistically, since it doesn’t support anything but a scroll layout (which means that there is no good visual way to create the advanced layouts I will discuss shortly). Also, the approach to saving documents is often unsatisfactory. The XmlWriter class simply uses a live, in-memory document and turns it into XAML, but unfortunately, it isn’t aware of many concepts that are important for large-scale Flow Document efforts, such as styles. As a result, the XAML faithfully preserves the look of the document, but it is often nasty and very large. The RichTextBox control is certainly useful, but don’t expect it to be a desktop publishing solution for on-screen content (although such an app is sorely needed).

Exploring Layout Possibilities

Now that you understand how to author and view Flow Documents, let’s return to the documents themselves and look at a few more features. Flow Documents are very sophisticated, and exploring all available features is beyond the scope of this article, but there are a few more things I would like to discuss.

One feature that always amazes me is called optimal paragraph. When enabled, this feature aims to distribute white space as evenly as possible within a given paragraph, resulting in a much improved reading experience. Optimal paragraph works particularly well in combination with hyphenation, another built-in feature that (surprise, surprise), performs on-the-fly hyphenation of either entire Flow Documents or individual paragraphs.

Turning optimal paragraph and hyphenation on is a straightforward operation:

<FlowDocument IsOptimalParagraphEnabled=”true” 
    IsHyphenationEnabled=”true”>

Figure 14 shows the same document rendered with these features enabled and disabled. The difference between the two versions is subtle, but significant. Note that the left version appears much calmer, mainly due to more evenly distributed but also overall reduced white space between words. Especially when reading large amounts of text on screen, this seemingly small difference is of crucial importance.

Figure 14 Optimal Paragraph and Hyphenation

Figure 14** Optimal Paragraph and Hyphenation **(Click the image for a larger view)

As you’ve seen already, the FlowDocumentReader control takes a multi-column approach to text rendering. This is another very important readability feature because people do not enjoy reading a single line across the entire width of a wide-screen display. The width of the actual columns varies depending on a number of factors, such as total available width for content display, zoom factor, and defined column width. By default, a Flow Document’s column width is 20 times the font size, which with the default font size is approximately 300 device-independent pixels (3 1/8 inch on a size-accurate display). You can override this default setting easily:

<FlowDocument ColumnWidth=”400”>

This results in columns with a width of roughly 400 pixels. However, there are other factors that impact the actual width. If the zoom is at 50 percent for instance, the actual column width is only 200 pixels. Also, the column width as it stands is to be seen more as a minimum column width. This means that if the total width available is 900 pixels, the rendered result contains two columns that are wide enough to fill up the entire 900 pixels, making each column wider than the defined 400 pixels. This is usually desirable as it results in a very appealing rendering. However, if you don’t like that behavior and want columns that are truly 400 pixels wide, you can make sure that the column widths are not flexible:

<FlowDocument ColumnWidth=”400” IsColumnWidthFlexible=”false”>

Now, all the columns are exactly 400 pixels wide (at 100 percent zoom), and remaining space is just left as white space.

Another column-related setting you may want to play with is the gap between columns. This can be adjusted through the ColumnGap property (the setting is also based on device-independent pixels):

<FlowDocument ColumnGap=”25”>

A related setting is the column rule, which allows for the definition of a visual element between the columns. Consider this example (the result of which can be seen in Figure 15):

Figure 15 Flow Document with a Simple Rule between Columns

Figure 15** Flow Document with a Simple Rule between Columns **(Click the image for a larger view)

<FlowDocument ColumnRuleWidth=”5” ColumnRuleBrush=”Red”>

Of course in many publications, documents are not just laid out in simple columns. Often, other elements exist that are taken out of the regular flow. You have already seen such examples with the images placed inside documents. Figure 12 shows an arrangement that is popular with graphic artists. The image sits between two columns, with the text flowing around it, putting the image squarely in the middle of the content without interrupting the flow much in either column. This is a common layout choice that simply hasn’t been available in dynamic on-screen reading environments I’m aware of prior to Flow Documents.

The key to the creation of such layouts is the figure block, which allows for the definition of content that does not flow with the rest of the document. Putting an image inside of a figure tag is one example, but there are many other uses for figures. For instance, you could use a figure to define a heading that stretches across the entire width of the document:

<Paragraph>
  <Figure HorizontalAnchor=”ContentLeft” VerticalAnchor=”ContentTop” 
          Width=”1Content”>
    <Paragraph FontSize=”36” FontWeight=”Bold”>Go With 
        The Flow</Paragraph>
  </Figure>
  Windows Presentation Foundation in Windows Vista provides a great set
    of features.
</Paragraph>

In this code the figure contains another paragraph, which is the text used as the headline. Note that there are some handy properties you can use to create advanced, flexible documents. Consider the width of the figure, for instance. Rather than setting the width to a certain number of pixels, I set it to be exactly the width of the content, which will automatically adjust the figure’s width to however wide the entire content is.

Take a look at Figure 16. There you’ll notice that the heading (positioned through a figure) is set to span across the width of the entire content, which pushes all four columns down. The image is horizontally and vertically anchored to the page center.

Figure 16 Heading Spans Four Columns

Figure 16** Heading Spans Four Columns **(Click the image for a larger view)

Note that figures with a width relative to the content do not always have to be as wide as the content. The following example for instance sets the width of the figure to be 75 percent of the width of the content:

<Figure Width=”0.75Content”>

The width can also be relative to other things, for instance, to the width of columns. The following example figure is always two columns wide (unless only one column is displayed, in which case the width is reduced to one column):

<Figure Width=”2Column”>

Of course, figure heights can be defined in a similar fashion (although often figures just grow vertically with the content).

Another important aspect is the position of the figure. In the code snippet, it is set to be horizontally anchored to the left and vertically to the top. This means that the figure appears at the top-left corner of the current page of content, regardless of where it is actually defined. In this example, the figure was defined as the first element of the document anyway, but even if there would have been paragraphs before that heading, it would have been moved up and to the left due to these settings. The photos in Figure 12 and Figure 16 have been moved between the columns in a similar fashion, by setting its horizontal anchor to "PageCenter". (The available property values for all these settings can be found in the WPF documentation).

You may have noticed that there was a lot of manual coding. For instance, whenever the font face needs to be changed, you add that information to a block or an inline. So far, this has not been a huge problem since most of the examples were small. However, if you have a 50-page chapter of a book and would like to change the font in every paragraph, then doing so manually every time becomes tedious. Luckily, there is a better approach: like everything else in WPF, Flow Documents support styling. Styles can be defined as named resources in the actual Flow Document. Here is a style that defines font information:

<FlowDocument>
  <FlowDocument.Resources>
    <Style x:Key=”MyStyle”>
      <Setter Property=”TextElement.FontSize” Value=”12” />
      <Setter Property=”TextElement.FontFamily” Value=”Bodoni MT” />
    </Style>
  <FlowDocument.Resources>
  ...
</FlowDocument>

The style can then be applied to paragraphs (and other elements) in the following fashion:

<Paragraph Style=”{StaticResource MyStyle}”>The quick... </Paragraph>

Due to the nature of Flow Documents, styles are very commonly used. I recommend that for anything but the simplest scenarios, you define most of your formatting options in styles, rather than with attributes on individual inlines. Styles keep your documents compact and also make them easier to maintain.

Above and Beyond

I hope this article provided you with a bit more than just a fundamental understanding of Flow Documents and their capabilities, and piqued your interest. There are many more advanced features that are definitely worth looking into, including sophisticated styling of viewer controls, subclassing and extending documents, blocks, and inlines, digital rights management, the text and ink annotation feature, and advanced font formatting.

Markus Egger is an international speaker and the publisher of CoDe Magazine. Markus is also the President and Chief Software Architect of EPS Software Corp., consulting for object-oriented development, Internet development, B2B, and Web services. See his blog at www.markusegger.com/blog.