I want you to perform a little experiment. Go into
the XAML file of the TelephonicConversation project and insert the
following setting into the ScrollViewer
tag:
HorizontalScrollBarVisibility="Visible"
Almost immediately you’ll
see a startling change. All the TextBlock elements become long single lines of text with no wrapping.
What happened? How does setting a property on the ScrollViewer have
such a profound effect on the individual TextBlock
elements?
In a sense, this behavior shouldn’t be surprising: If
the ScrollViewer
has a horizontal scrollbar, it must exist for some purpose, and it has
no purpose if the words of each TextBlock wrap into paragraphs. If the horizontal scrollbar is
to have some function, then the paragraphs should consist of single
lines.
But it would be nice to have a
better grasp on this actual mechanism, and not only to understand this
particular peculiarity. Getting a good feel for the layout system is one of
the most important Silverlight programming skills you can acquire. The layout system is very
powerful, but for the uninitiated, it can also seem quite strange.
Layout in Silverlight is a
two-pass process starting at the top of the visual tree and working down through all the
elements’ children. In a Silverlight phone application, it begins with
the PhoneApplicationFrame, then the PhoneApplicationPage, then
most likely a Grid and then (usually) a
StackPanel and a second Grid. In
Telephonic Conversation, the process continues into the ScrollViewer, which
probably contains its own Border, and
then eventually the StackPanel, and
finally the TextBlockTextBlock elements
have no children so that’s the end of the line. elements. These
During the first pass, every
element in the tree is responsible for querying its children to obtain
their desired size.
In the second pass, elements are responsible for arranging their
children relative to their surface. The arrangement can be trivial or
complex. For example, a Border has only one child and need only take account of
its own BorderThickness to determine where to position that child
relative to itself. But Panel derivatives must arrange their children in
unique ways.
When a parent queries the size of its
children, it effectively says “Here’s an available size for you. How big
do you want to be?”
and each child calculates its desired size. All sizes are in the form
of a Size structure with Width and Height properties. If that child itself has children,
then the child must determine its own size by querying its children’s
sizes, until the process gets down to elements like TextBlock that have no children.
Elements determine their
own size in various ways depending on the nature of the element. A TextBlock, for
example, might be displaying a long piece of text and might have its TextWrapping property set to Wrap. In that case, the TextBlock looks at the Width property of the
available size
and determines where lines should break. It then knows how many lines it
needs to display and how much vertical space is required for all those
lines. This is how the TextBlock
calculates its desired size.
But there’s also an odd
complication: A parent presents its children with an available size using the Size
structure, which has two properties named Width
and Heightdouble. Sometimes the
parent could set the Width or Height (or both) to that special
floating-point value Double.PositiveInfinity. The parent is basically saying: “Child, I am offering
you an infinite width [or an infinite height, or both] to play around
in. How much of that do you need?” of type
The child cannot respond “I
want it all!” as children sometimes tend to do. That’s not allowed. The
child must claim a desired size that is finite and non-negative.
This is how the StackPanel
queries the size of its children. A
vertical StackPanel
offers to each of its child an available size with a width that is
equal to its own width, but a height of infinity.
But there’s a paradox here: Some
elements, such as the TextBlock and Image, have some kind of intrinsic size, which
is the size of the formatted text or the size of the unscaled bitmap.
Others, like the Ellipse, do not have
an intrinsic size. When an Ellipse is given a specific size, it will display itself
at that size. But when the Ellipse is offered an infinite size, it has no choice but
to shrink itself into nothingness.
To understand the
precise mechanism at work here, it will be extremely useful to actually
create some simple panels.