Text Layout
The simplest element for presenting text is TextBlock,
which is efficient but has limited functionality. TextFlow offers more
advanced typography and layout functionality but with slightly more overhead.
2.7.1. TextBlock
TextBlock is useful for putting short blocks of text
into a user interface. If you don't require any special formatting of the text,
you can simply wrap your text in a TextBlock:
<TextBlock>Hello, world!</TextBlock>
However, even though TextBlock is the simplest
text-handling element, it is capable of a little more than this example shows.
For example, it has a set of properties for controlling the font, shown in
Table 2-2.
Table 2-2. TextBlock font properties
|
Property
|
Usage
|
FontFamily
|
Typeface name; e.g., "Times
New Roman" or "Lucida Console"
|
FontSize
|
Size of the font
|
FontStretch
|
Used to indicate condensed or
expanded versions of a typeface, where available
|
FontStyle
|
Normal, Oblique, or Italic
|
FontWeight
|
Weight; e.g., "Bold," "Light"
or "Black"
|
2.7.1.1. Text wrapping
The TextBlock is capable of wrapping text across
multiple lines where necessary. This is enabled with the TextWrap property,
as shown in Example 2-20.
Example 2-20. Text wrapping
<TextBlock FontSize="24" TextWrap="Wrap">
Split across multiple lines
</TextBlock>
The result is shown in
Figure 2-28. (If you try this, note that you will only see wrapping if
the TextBlock is in a sufficiently narrow space to need it. Even with
wrapping enabled, the TextBlock prefers to put everything on one line
when it can.)
This wrapping facility can also be used to flow other elements
across multiple lines. The TextBlock can have a mixture of text and
arbitrary elements, as you can see in
Example 2-21.
Example 2-21. Flowing non-text elements
<TextBlock TextWrap="Wrap">
<Button>Split</Button>
<CheckBox>across</CheckBox>
<TextBox>multiple</TextBox>
lines
</TextBlock>
The results are shown in
Figure 2-29. Elements are treated in exactly the same way as wordsif
they are too wide to fit on the current line, they will be moved onto the next
line.
2.7.1.2. Text styles
As well as supporting a mixture of text and other elements, the TextBlock
is also able to support a mixture of text styles .
If you would like a word to be in italic, bold, or a different typeface, this
is easily achieved. WPF defines a number of so-called "inline" elements for
modifying the appearance of text within a single control. These are shown in
Table 2-3.
Table 2-3. Inline text elements
|
Element name
|
Usage
|
AccessKey
|
Underlines the section in the
style of an accelerator key.
|
Bold
|
Makes the text bold.
|
Hyperlink
|
Displays the text as a
hyperlink.
|
Italic
|
Makes the text italic.
|
Subscript
|
Shows the text raised, and in
a smaller typeface.
|
Superscript
|
Shows the text lowered, and in
a smaller typeface.
|
Underline
|
Underlines the text.
|
Example 2-22 shows
a few of these inline elements in use.
Example 2-22. Inline elements
<TextBlock FontSize="18">
<AccessKey>T</AccessKey>his <Italic>is</Italic> <Bold>rather</Bold>
<Underline>messy</Underline>. <Hyperlink>www.example.com</Hyperlink>
</TextBlock>
2.7.2. Text and Whitespace
As Figure 2-30
shows, there's a trap for the unwary when using inline elements. XML parsing
does not necessarily preserve whitespace, because many XML documents use
whitespace for formatting. For example, most of the XAML in this book uses
indentation to make the structure of the markup easier to follow. When there is
nothing but whitespace between two elements, the XAML compiler strips out this
space by default. Unfortunately, this is undesirable in this particular
exampleit has removed the space we wanted between the words. (Notice that it
has only affected adjacent inline elements. We can still see a space between
"This" and "is," and between the "." and "www.example.com"
because in those cases, there was more than just whitespace between the inline
elements.)
The solution to this is a standard XML feature. You can mark
elements with the xml:space="preserve" attribute to indicate that the
whitespace it contains is significant.
Example 2-23 shows a modified version that fixes the problem.
Example 2-23. preserve attribute
<TextBlock FontSize="18" xml:space="preserve"><AccessKey>T</AccessKey>his
<Italic>is</Italic> <Bold>rather</Bold> <Underline>messy</Underline>.
<Hyperlink>www.example.com</Hyperlink></TextBlock>
 |
Note that the code in
Example 2-23 has been split across multiple lines in order to fit into
the book. If you try this, type it in on a single line.
This solves the problem, as
Figure 2-31 shows. However, it has come at the cost of making the
markup much more untidy. We cannot add any spaces, tabs, or line breaks in
order to make the markup easier to read, as these will now be faithfully
represented onscreen.
|
|
As a compromise, you could use the xml:space attribute
more selectively. Rather than applying it to the top level, you could apply it
to just those elements that are causing you problems. This might make the
markup a little more verbose, but it does give you more freedom to format it
however you like. Example 2-24
uses this technique, and produces the same output as
Example 2-23.
Example 2-24. space attribute
<TextBlock FontSize="18">
<AccessKey>T</AccessKey>his <Italic>is</Italic>
<Bold xml:space="preserve"> rather </Bold>
<Underline>messy</Underline>. <Hyperlink>www.example.com</Hyperlink>
</TextBlock>
If you place a line break in a section of text marked with xml:space="preserve",
that line break will be honored on the display. If you would like to add a line
break without using xml:space="preserve", you can add a LineBreak
element.
2.7.2.1. Text alignment
The TextBlock offers a TextAlignment property
that allows you to choose whether text is left-aligned, right-aligned,
centered, or justified. Example 2-25
shows all of these styles. Figure
2-32 shows the results.
Example 2-25. Text alignment
<StackPanel Orientation="Horizontal">
<TextBlock Width="80" TextAlignment="Left" TextWrap="Wrap" Margin="15">
This text is left-aligned. Its right edge is ragged.
</TextBlock>
<TextBlock Width="80" TextAlignment="Right" TextWrap="Wrap" Margin="15">
This text is right-aligned. Its left edge is ragged.
</TextBlock>
<TextBlock Width="110" TextAlignment="Center" TextWrap="Wrap" Margin="15">
This text is centered. Both edges are ragged, in a symmetrical way.
</TextBlock>
<TextBlock Width="120" TextAlignment="Justify" TextWrap="Wrap" Margin="15">
This text is justified. Both edges are straight. Words are spaced out
in order to achieve this
</TextBlock>
</StackPanel>
The ability of the TextBlock to support wrapping,
arbitrary inline UI elements, line breaking, and a mixture of text styles makes
it useful for a wide variety of simple text scenarios. However, its layout
capabilities are somewhat limited compared to the TextFlow element.
2.7.3. TextFlow
TextFlow can perform all of the same duties as TextBlock,
but provides much more sophisticated layout support. As well as supporting all
of the inline elements described in the previous section, TextFlow adds
support for so-called "block" elements. These are listed in
Table 2-4.
Table 2-4. Block element types
|
Element name
|
Usage
|
Paragraph
|
A paragraph
|
List
|
Numbered or bulleted list
|
Table
|
A table of information
|
Floater
|
An element arranged outside of
the main flow, and which everything else can flow around
|
Figure
|
Similar to a Floater,
but with a caption, and with extra control over exactly where the element is
placed
|
2.7.3.1. Paragraph
The Paragraph element doesn't strictly enable anything
that couldn't be achieved with the LineBreak element in a TextBlock.
However, it is often more convenient, as paragraphs are part of the logical
structure of the text. It usually makes more sense to deal with paragraphs
rather than the gaps between them.
For example, if a particular paragraph represents a heading, you
can apply a font or style to the Paragraph element containing the
heading. (Paragraph supports all of the same font properties as TextBlock.)
Choosing a font for a LineBreak, on the other hand, would not be
useful.
2.7.3.2. List
The List element allows numbered or bullet lists to be
added to the text. The same syntax is used whether you require numbers or
bullets. The style is selected with the MarkerStyle property, as shown
in Example 2-26.
Example 2-26. List marker styles
<TextFlow FontSize="18">
<List MarkerStyle="Decimal">
<ListItem><Paragraph>First item</Paragraph></ListItem>
<ListItem><Paragraph>Second item</Paragraph></ListItem>
</List>
</TextFlow>
The MarkerStyle property supports several styles, both
numeric and bulleted. Figure 2-33
shows all of them in use.
2.7.3.3. Table
The Table element presents information in tabular form.
It may seem curious for WPF to offer both a Table and a Grid element,
but they address slightly different problems. Although there is some overlap in
functionality, neither offers a complete subset of the other's functionality.
The Grid is designed entirely for laying out elements and can be used
to arrange user interfaces that don't look obviously tabular (e.g., it is
useful for a wide range of dialog layouts). The Table is designed
exclusively for presenting tables of information.
Table is less flexible than Grid when it comes
to layout. Each cell in a Table must be occupied by exactly one item.
(Items can still span cells.) However, it does provide some features not
offered by Grid. It has support for header and footer information. And
if a table is viewed in a context that supports pagination, such as printing or
the DocumentViewer control, Table has special support for
being split across multiple pages.
Example 2-27 shows
a Table with headers. The result is shown in
Figure 2-34.
Example 2-27. Table
<Table CellSpacing="6">
<TableHeader>
<TableRow FontSize="24" FontWeight="Bold">
<TableCell ColumnSpan="3" TextAlignment="Center" >
Ice Cream
</TableCell>
</TableRow>
<TableRow FontWeight="Bold" FontSize="18" Background="LightGray">
<TableCell>Type</TableCell>
<TableCell>Description</TableCell>
<TableCell>Availability</TableCell>
</TableRow>
</TableHeader>
<TableBody>
<TableRow>
<TableCell>Chocolate</TableCell>
<TableCell>Yummy</TableCell>
<TableCell>Widespread</TableCell>
</TableRow>
<TableRow>
<TableCell>Cookie Dough</TableCell>
<TableCell>Extra yummy</TableCell>
<TableCell>Scarce - Ian ate it all</TableCell>
</TableRow>
<TableRow>
<TableCell>Artichoke</TableCell>
<TableCell>Gruesome</TableCell>
<TableCell>Rarely available</TableCell>
</TableRow>
</TableBody>
</Table>
2.7.3.4. Floater
A Floater is a block that is not laid out as part of
the overall flow of the text. It is usually arranged to one side, and the rest
of the content of the TextFlow flows around it.
Example 2-28 shows
a typical use of a Floater. This TextFlow contains some Paragraphs
of text. It has one Floater acting as a sidebar.
Figure 2-35 shows the results.
Example 2-28. TextFlow with Floater
<TextFlow>
<Paragraph FontWeight="Bold">Metasyntactic Variables</Paragraph>
<Paragraph>
<Floater Background="LightBlue" Margin="10" Width="230">
<Paragraph FontWeight="Bold">Foo and Bar</Paragraph>
<Paragraph>
Many developers are unaware of the origin of
the most popular placeholders,'Foo' and 'Bar'.
</Paragraph>
<Paragraph>
They originate from the Air Force. They are a
corruption of FUBAR, the polite expansion of
which is 'Fouled Up Beyond All Recognition.'
</Paragraph>
</Floater>
</Paragraph>
<Paragraph>
In describing software, we must often describe
constructs or processes in the most general of
terms. Using concrete examples is not always wise,
because specific details of the example can get
in the way.
</Paragraph>
<Paragraph>
English offers only a limited number of nouns
suitable for discussing abstract constructs.
You can only use the word 'thing' so many times
without sounding stupid, so most software
practitioners use words such as 'Foo' and 'Bar'
to augment the number of abstract nouns available.
These words are sometimes referred to as
'metasyntactic variables'
</Paragraph>
</TextFlow>
By default, a Floater will appear to the right of the
main text. However, you can change this with the HorizontalAlignment property,
which supports three options: Left, Center, and Right.
Example 2-29 shows all three
in use, and Figure 2-36 shows
the results.
Example 2-29. Floater alignment
<TextFlow>
<Paragraph>
<Floater Background="Yellow" Margin="10,0" HorizontalAlignment="Left">
<Paragraph FontWeight="Bold">Left Floater</Paragraph>
</Floater>
</Paragraph>
<Paragraph>
This is some normal text.
</Paragraph>
<Paragraph>
<Floater Background="Yellow" Margin="10,5" HorizontalAlignment="Center">
<Paragraph FontWeight="Bold">Center Floater</Paragraph>
</Floater>
</Paragraph>
<Paragraph>
This is some more normal text. Notice how it flows both
sides of the centered floater.
</Paragraph>
<Paragraph>
<Floater Background="Yellow" Margin="10,0" HorizontalAlignment="Right">
<Paragraph FontWeight="Bold">Right Floater</Paragraph>
</Floater>
</Paragraph>
<Paragraph>
This is yet more normal text.
</Paragraph>
</TextFlow>
2.7.3.5. Figure
Figure provides a similar service to Floater;
it allows a block to be put outside of the main text, which the main text will
flow around. However, you can give the TextFlow more freedom to decide
where to position a Figure than a Floater.
Figure has a HorizontalAnchor property that
indicates where the figure should appear. This does a very similar job to the
floater's HorizontalAlignment, and offers the same three options:
left, middle and center. However, unlike the floater, you can specify how far
the figure can be moved vertically in order to fit it into the text. By
allowing WPF some flexibility for vertical placement, it may be able to do a
better job of arranging the document onto the available screen space than would
otherwise be possible.
For example, you can specify PageLeft, PageCenter,
or PageRight. These indicate that the figure should be placed to the
left of, in the center of, or to the right of the page, respectively. This
tells the TextFlow that any vertical position is allowed so long as it
the figure is displayed on the same page as the paragraph after the figure. ContentLeft,
ContentCenter, and ContentRight allow the figure to appear at
any vertical location. (Pagination is only relevant when printing, or using a
paginating control such as the DocumentViewer. When viewing text
without pagination, Page and Content positioning are equivalent.)
You can choose to be more restrictive by specifying ParagraphLeft,
ParagraphCenter, or ParagraphRight anchor values. These
require the figure to be displayed next to the paragraph that follows the
figure. These horizontal anchor values are equivalent to the three HorizontalAlignment
options we saw with Floater.
There is also a VerticalAnchor property. This lets you
specify the vertical positioning you require, allowing WPF some latitude with
the vertical location. The available options are essentially the same as for
the horizontal anchor, but with Left, Center, and Right
replaced with Top, Center, and Bottom.
Armed with the TextFlow, we are now in a position to
complete the layout of our document viewer. The TextFlow offers all of
the layout features we require to provide richly formatted text. All we need to
do is wrap it in a ScrollViewer to enable scrolling through long
documents.
 |