7.4. Transformations
Support for high-resolution displays is an important feature of
WPF. This is enabled in part by the emphasis on the use of scalable vector
graphics rather than bitmaps. But, as experience with GDI+ and GDI32 has shown,
if scalability is not integrated completely into the graphics architecture,
resolution independence is very hard to achieve consistently in practice.
WPF's support for scaling is built
in at a fundamental level. Any element in the user interface can have a
transformation applied, making it easy to scale or rotate anything.
All user-interface elements have a RenderTransform property
of type transform. This is an abstract base class, from which there
are derived classes implementing various affine transformations
:
rotation , scaling, translation
, and shearing . All of these are just convenience
classesall supported transformations can be represented by the MatrixTransform
class. This contains a 3 x 3 matrix, allowing any affine transformation to be
used.
Example 7-44 shows
the use of the RenderTransform property.
Example 7-44. Using RenderTransform
<StackPanel Orientation="Horizontal">
<TextBlock>
<TextBlock.RenderTransform>
<TransformGroup
>
<ScaleTransform ScaleX="2" ScaleY="2" />
<RotateTransform Angle="10" />
</TransformGroup>
</TextBlock.RenderTransform>
Hello,
</TextBlock>
<TextBlock>world</TextBlock>
</StackPanel>
Notice that a TRansformGroup has been used here to
combine the effects of two transforms. (Note that the rotation angle is
specified in degrees here.) The results are shown in
Figure 7-53.
In markup, you would not normally write transforms out in full
as Example 7-44 does, because
you can use the abbreviated string syntax shown in
Example 7-45.
Example 7-45. Using abbreviated transform syntax
<StackPanel Orientation="Horizontal">
<TextBlock RenderTransform="scale 2,2 rotate 10">Hello,</TextBlock>
<TextBlock>world</TextBlock>
</StackPanel>
The RenderTransform property allows you to specify a
sequence of transforms, which will be converted into suitable transform
objects for you.
RenderTransform changes the appearance of an element
but has no effect on layout. Notice how in
Figure 7-53 the "Hello" TextBlock runs underneath the "world"
block. These elements are both in a horizontal StackPanel, so you
would normally expect the second element to be completely to the right of the
first element, instead of overlapping. However, RenderTransform is
effectively invisible to the layout logic, so StackPanel has arranged
the elements as though the transform were not in place.
In practice, you will often want the transformation to be taken
into account by the layout system. In this case, you should not use the RenderTransform
property. Instead, you should use the LayoutTransform property, as
Example 7-46 shows.
Example 7-46. Using LayoutTransform
<StackPanel Orientation="Horizontal" Margin="10"
<TextBlock LayoutTransform="scale 2,2 rotate 10">Hello,</TextBlock>
<TextBlock>world</TextBlock>
</StackPanel>
The LayoutTransform property applies the transformation
in a way that is visible to the layout system. As
Figure 7-54 shows, this means that the StackPanel now
allocates enough space for the transformed text, and the two elements no longer
overlap.
You can apply any number of transforms at any place in the
visual tree. Figure 7-55 shows
a user interface where the main area has been rotated and slightly enlarged,
but part of its contents have been rotated in the opposite direction and
reduced. Obviously this particular example isn't terribly useful, but it does
show that transformations can be added at arbitrary places. Moreover, layout
continues to work correctlythe main user interface is arranged using a Grid,
and the rotated inner contents are in a nested Grid. If the main
window is resized, both of these grids rearrange their contents correctly,
unfazed by the presence of the transforms.
This thorough support for transformations is important, not
because it enables wacky layouts like
Figure 7-55, but because you can depend on transformations to work
consistently and reliably.
 |