Tải bản đầy đủ (.pdf) (93 trang)

Programming C# 4.0 phần 10 pdf

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (10.42 MB, 93 trang )

Content="Row 1, 3 columns wide" />
<Button Grid.Column="0" Grid.Row="2"
Grid.ColumnSpan="3"
Content="Row 2, 3 columns wide" />
<Button Grid.Column="1" Grid.Row="3"
FontSize="50"
Content="(3, 1)" />
</Grid>
Figure
20-2
shows how this looks. The four rows are fairly clear—each button belongs
to just one row. The columns are less obvious—you can see all three clearly in the first
row, because there’s one button in each, but the next two rows contain just one button
each, spanning all three rows. And the final row contains a single button in the second
column.
Figure 20-2. Grid children
The Grid knows
which columns and rows elements belong to, and how many they span,
because each button in Example 20-4 has properties that control this. The
Grid.Column and Grid.Row properties do what their names suggest, while the
Grid.ColumnSpan and Grid.RowSpan properties determine how many grid cells the ele-
ment occupies. The column and row default to 0, while the spans default to 1.
740 | Chapter 20: WPF and Silverlight
These properties use another special Xaml feature called attached prop-
erties. An attached property is one defined by a different type (e.g.,
Grid) than the object it is applied to (e.g., Button). The attached prop-
erties in Example 20-4 are attributes, but you can also set attached
properties with the property element syntax shown earlier—for
example, if a <Grid> element could contain a <ToolTipService.Tool
Tip> element, to set the attachable ToolTip property defined by the
ToolTipService class.


While Silverlight, WPF, and Xaml support the idea that properties don’t
necessarily have to be defined by the object on which they are set, C#
has no syntax for this. So classes that define attachable properties also
define get and set methods to enable those properties to be used from
code. For example, the Grid class offers SetColumn, SetRow, and so on.
The rows and columns in Figure 20-2 are different sizes. This is because of the settings
on the <RowDefinition> and <ColumnDefinition> elements. The first column’s Width has
been set to Auto, so it takes its size from the widest child in that column. In this case,
only one child belongs exclusively to that column, so the column is exactly wide enough
to hold it. The other two columns are at their default width, the value 1*, which causes
them to share the remaining space equally. The rows use similar features, except the
first row has a fixed height of 30, so it ignores the size of the content and makes every
element 30 pixels high. The final row is Auto sized, and since its content has a large font
size, it ends up being fairly tall. And the middle two rows use so-called star sizing, so
as with the second and third columns, they end up sharing the space left over. However,
since they have different star size values—1* and 2*—they get different amounts of
space. The 2* row gets to be twice the height of the 1* row. Note that the ratios are all
that matter with star sizing—changing 1* and 2* to 10* and 20* would not change the
outcome in this example, because 20* is still twice as large as 10*.
So as you can see, a grid can use fixed sizes, it can base sizes on the content at hand, or
it can divide the available space proportionally. This makes it a pretty flexible layout
mechanism. You can build dock-style layouts where elements are aligned to the top,
bottom, left, or right of the available space through the use of Auto sized rows and
columns, and by making elements span all the available rows when docking to the left
or right, or all the columns when docking to the top or the bottom. You can also stack
elements horizontally or vertically by using multiple rows or columns with Auto sizes.
And as we’ll see, it’s even possible to exercise precise control over the size and position
of elements within the grid. One slight problem is that your Xaml can get a little verbose
when using grids. So there are some simpler panel types.
StackPanel arranges children in a vertical or horizontal stack. Example 20-5 shows a

StackPanel with its Orientation set explicitly to Vertical. You can doubtless guess how
to make a horizontal stack. (In fact, vertical stacks are the default, so you could leave
the orientation out from Example 20-5 without changing its behavior.)
Elements and Controls | 741
Example 20-5. Vertical StackPanel
<StackPanel Orientation="Vertical">
<Button Content="Buttons" FontSize="30" />
<Button Content="in" />
<Button Content="a" />
<Button Content="stack" />
</StackPanel>
Figure 20-3
shows the result. Notice that in the direction of stacking—vertical in this
example—the behavior is similar to the Auto height grid rows, in that each row has
been made tall enough to accommodate the content. In the other direction, the elements
have been stretched to fill the available space, although as we’ll see shortly, you can
change that.
Figure 20-3. Vertical StackPanel
The Canvas
panel
takes an even simpler approach: it doesn’t have a layout strategy, and
it simply puts elements where you tell it to. As Example 20-6 shows, just as Grid offers
attachable properties to specify which grid cells elements occupy, Canvas defines at-
tachable Left and Top properties that specify where the elements should appear.
Example 20-6. Explicit positioning with Canvas
<Canvas>
<Button Content="Buttons" FontSize="30" />
<Button Canvas.Left="20" Canvas.Top="40"
Content="on" />
<Button Canvas.Left="80" Canvas.Top="40"

Content="a" />
<Button Canvas.Left="60" Canvas.Top="100"
Content="Canvas" />
</Canvas>
As Figure 20-4 shows, the exact positioning possible with a Canvas has let us position
elements so that they overlap. (This figure includes some of the browser chrome to
illustrate that positions are relative to the top-left corner of the Canvas.) Notice that the
Canvas sizes children based on how much space they require—similar to the Auto rows
and columns, but in this case the buttons are sized to content in both dimensions.
Unless you specify explicit widths and heights, a Canvas will attempt to give each child
exactly as much space as it requires.
742 | Chapter 20: WPF and Silverlight
Silverlight and WPF have extensible layout systems, so you can derive your own types
from Panel or
use libraries that offer other panels. For example, Microsoft offers the
Silverlight Toolkit, a free library you can download in source or binary form from http:
//silverlight.codeplex.com/, which defines various controls, panels, and other useful
components. This includes two panels, both based on panels that are built into WPF.
There’s WrapPanel, which lays out its children in much the same way that text is word-
wrapped in web browsers and word processors—items are arranged from left to right
until all the space is used up, at which point the panel starts on a new line. And there’s
also DockPanel, which lets you arrange elements by stacking them up against the left,
right, top, or bottom of the panel. (DockPanel doesn’t do anything Grid can’t do, but it
can be slightly simpler to use.)
Layout in WPF and Silverlight is not just about panels. Panels define the strategy by
which elements are allocated a layout slot—the area on-screen in which they must fit
themselves. But properties are available on all elements—regardless of the panel in
use—that can influence both how big the layout slot is and what the element does with
the space it is offered.
General-purpose layout properties

All elements have common properties that influence layout. There are Width and
Height properties that let you specify an explicit size, rather than basing the size on the
content or the available space. This is important for elements that don’t otherwise have
an intrinsic size. Textual content has a natural size, but some graphical elements such
as Ellipse and Rectangle don’t. If you were to create an Ellipse without setting the
height and put it in a vertical StackPanel it would vanish, because the StackPanel asks
it to calculate the minimum amount of space it requires, and if you have not specified
any constraints, that’ll be zero. So elements with no intrinsic size usually have an ex-
plicit Width and Height, or you might use MinWidth and MinHeight to ensure that they
never vanish entirely, but are able to expand to fill whatever space is available—some
layouts will end up with more space than needed if the user resizes a window, so it can
be useful to have a layout that adapts. MaxWidth and MaxHeight let you specify upper
limits on just how far elements will expand.
Figure 20-4. Buttons on a Canvas
Elements and Controls | 743
The various width and height properties are useful when an element is being asked to
determine its own size, such as in Auto sized grid cells. But sometimes an element’s
layout slot size is imposed on it—for example, if your Silverlight user interface is con-
figured to fill the entire browser window, the user is in charge of how big it is. This is
sometimes referred to as constrained layout—this describes situations where the layout
system has to make things fit a predetermined space, rather than trying to work out
how much space is required. Most user interfaces contain a mixture of constrained and
unconstrained layout—the top level of the UI is usually constrained by window size,
but you might have individual elements such as text blocks or buttons that have to be
large enough to display their content.
When elements that have no intrinsic size are put in a constrained lay-
out, they will fill the space available if you don’t set the width and height.
For example, if you put an Ellipse as the only element of the root
Grid layout element, and you don’t set any of the width or height prop-
erties, it will fill the whole Silverlight application UI.

You can even get a mixture of constrained and unconstrained layouts on one element.
In Figure 20-3, we saw a vertical stack of elements, and vertically, each one’s size was
based on its content—since the elements are free to size themselves it means we have
unconstrained layout vertically. But the elements are all the same width regardless of
content, indicating that constrained layout was in use horizontally. Stack panels always
work this way—children are unconstrained in the direction of stacking, but are con-
strained to have the same sized layout slots in the other direction.
When an element has more space than it needs due to constrained layout, additional
properties that determine what the element does with the excess space come into play.
The HorizontalAlignment attribute lets you position the element within its slot. Exam-
ple 20-7 shows a modified version of Example 20-5, specifying each of the four
HorizontalAlignment options.
Example 20-7. Horizontal alignment
<StackPanel Orientation="Vertical">
<Button Content="Buttons" FontSize="30"
HorizontalAlignment="Left" />
<Button Content="in"
HorizontalAlignment="Right" />
<Button Content="a"
HorizontalAlignment="Stretch" />
<Button Content="stack"
HorizontalAlignment="Center" />
</StackPanel>
744 | Chapter 20: WPF and Silverlight
Figure 20-5 shows the results. As before, each child has been given a layout slot that
fills the whole width of the StackPanel, but all except the third row have been sized
to content, and have then positioned themselves within their slot based on the
HorizontalAlignment property. The third button still fills the whole of its row because
its alignment is Stretch. That’s the default, which is why elements fill their whole layout
slot unless you specify an alignment. VerticalAlignment works in much the same way,

offering Top, Bottom, Center, and Stretch.
Figure 20-5. Horizontal alignment
The alignment properties do something only when the layout slot is
larger than
the element requires. When an element has been given a slot
exactly as large as it asked for in either the horizontal or vertical dimen-
sion, the corresponding alignment property does nothing. So setting
VerticalAlignment on the child of a vertical StackPanel does nothing—
the layout slot is already exactly as tall as the element requires, so the
element is simultaneously at the top, the bottom, and the center of the
slot.
Another very important ubiquitous layout property is Margin—this lets you specify the
amount of space you’d like between the edge of an element and the boundary of its
layout slot. In unconstrained layout, a margin will cause an element to be given a larger
slot than it would otherwise have had, while in constrained layout, it causes an element
to fill less of the slot than it otherwise would have. Example 20-8 illustrates this within
a vertical StackPanel—since this uses constrained horizontal layout and unconstrained
vertical layout for its children, we’ll see both effects.
Example 20-8. Buttons with Margin properties
<StackPanel Orientation="Vertical">
<Button Content="Buttons" FontSize="30" />
<Button Content="in" Margin="10" />
<Button Content="a" Margin="20" />
<Button Content="stack" Margin="30" />
</StackPanel>
Elements and Controls | 745
In Figure 20-6, the first button fills the entire width because it has no margin. But each
successive button gets narrower, because each has a larger margin than the last. Since
the width is constrained, the layout system needs to make the buttons narrower to
provide the specified margin between the element’s edges and its layout slot. But since

the children here are unconstrained vertically, the margin has no effect on their vertical
size, and instead ends up adding increasing amounts of space between each element—
in the unconstrained case, Margin makes the slot larger.
Figure 20-6. Buttons with margins
Example 20-8 specifies
the margins as single numbers, denoting a uniform margin on
all four sides, but you can be more precise. You can provide two numbers, setting the
horizontal and vertical margins. Or you can provide four numbers, indicating the left,
top, right, and bottom

margins independently. This enables precise positioning of
elements within a Grid—it turns out that you don’t have to use a Canvas to specify the
position of an element. If you align an element to the left and the top, the first two
numbers in a margin effectively determine its position within the containing grid cell,
just as the attachable Canvas.Left and Canvas.Top properties work for children of a
Canvas. The interactive design surfaces in Visual Studio and Blend use this to let you
drag elements around on a grid and place them exactly where you want. It appears to
be a completely free form of layout, but if you inspect what these programs do to the
Xaml as you move elements around, they simply set the alignment properties appro-
priately and adjust the margins.
All of the layout features we’ve looked at so far take a rigidly rectangular approach—
everything is either strictly horizontal or strictly vertical. In fact, WPF and Silverlight
are a bit more flexible than that, thanks to their support for transforms.
‡ Yes, that is a different order than CSS. Silverlight and WPF follow the coordinate geometry convention of
specifying
pairs of coordinates as horizontal and then vertical measures—x before y. Hence left, then top,
followed likewise by right, then bottom.
746 | Chapter 20: WPF and Silverlight
Transforms
You can apply a transform to any element, modifying its size, position, and orientation,

or even skewing it. (If you’re familiar with the coordinate geometry features found in
most modern graphics system, you’ll recognize these as being the usual two-
dimensional affine transformations possible with a 2×3 matrix.
§
) Example 20-9 shows
another variation on our StackPanel example, with transforms applied to the children.
Example 20-9. Transforms
<StackPanel Orientation="Vertical">
<Button Content="Buttons" FontSize="30">
<Button.RenderTransform>
<ScaleTransform ScaleX="1.5" ScaleY="0.5" />
</Button.RenderTransform>
</Button>
<Button Content="in">
<Button.RenderTransform>
<RotateTransform Angle="30" />
</Button.RenderTransform>
</Button>
<Button Content="a">
<Button.RenderTransform>
<SkewTransform AngleX="30" />
</Button.RenderTransform>
</Button>
<Button Content="stack">
<Button.RenderTransform>
<TranslateTransform Y="-50" />
</Button.RenderTransform>
</Button>
</StackPanel>
As Figure 20-7 shows, the RenderTransform property Example 20-9 uses can mess up

the layout. The transform is applied after the layout calculations are complete, so the
ScaleTransform on the first button has had the effect of making it too large to fit—the
default HorizontalAlignment of Stretch is in effect here, so the button has been made
exactly as wide as the containing StackPanel, and then has been scaled to be 1.5 times
wider and 0.5 times higher, causing it to be cropped horizontally. Likewise, the ele-
ments that have been rotated and skewed have had corners cut off. WPF offers a
LayoutTransform property that takes the transform into account before performing lay-
out, which can avoid these problems, but Silverlight does not—you would need to
tweak the layout to get things to fit.
A transform applies not just to the target element, but also to all that
element’s
children.
For example, if you apply a RotateTransform to a
panel, the panel’s contents will rotate.
§ Strictly speaking, it’s a 3×3 matrix, but the final column is fixed to contain (0, 0, 1).
Elements and Controls | 747
This support for rotation, scaling, and shearing reveals that WPF and Silverlight are
designed to
support more graphically interesting user interface styles than traditional,
rigidly rectilinear Windows user interfaces. So this seems like a good time to look at
some of the graphical elements.
Graphical Elements
WPF and Silverlight support several kinds of graphical elements. The shape elements
provide scalable vector-oriented two-dimensional shapes. There are also various ways
to incorporate bitmap images. Video is supported through the media element. And
WPF and Silverlight both provide some support for 3D graphics, although they take
rather different approaches.
Shapes
Shape is the base class of various two-dimensional shapes. It’s an abstract class, and it
defines common properties such as Fill and Stroke to control how the interior and

outline of shapes are painted. Some of the derived classes are self-explanatory—it
doesn’t take much imagination to work out what Ellipse, Rectangle, and Line do.
Polyline, Polygon, and Path require a little more explanation.
Polyline lets you define a shape as a series of straight lines—you simply provide a list
of coordinate pairs defining each point the shape’s outline passes through. Polygon does
the same thing, but closes off the shape—it automatically joins the final point with the
first one. However, you rarely use either of these, because Path lets you do all this and
more. (Expression Blend never creates Polyline or Polygon elements—even if you create
a shape whose outline is made up entirely of straight edges, it still makes a Path. And
most Xaml export tools from programs such as Adobe Illustrator do the same. So in
practice, Path is the one you’ll come across. The other two exist because they are slightly
simpler to work with from code.)
Figure 20-7. Transformed buttons
748 | Chapter 20: WPF and Silverlight
Path lets you define a shape with any mixture of straight and curved segments in its
outline. Example 20-10 shows a Path made up entirely of straight edges.
Example 20-10. Path with straight edges
<Path Fill="Red" Stroke="Black"
StrokeThickness="5"
Data="M50,0 L100,50 50,100 0,50 z"
/>
The Data property defines the shape. It consists of a series of commands and coordi-
nates. The letters indicate the command—the initial M means Move to the specified
position, (50, 0) in this case. The L means draw a Line to the next coordinate. And since
this example has three coordinate pairs after the L, even though L requires only one,
that means we repeat the command—so that’s three straight line segments passing
through the coordinates (100, 50), (50, 100), and (0, 50). Each segment starts where
the previous one left off. Finally, the z indicates that we’d like to make this a closed
shape, so it will join that final point back up with the first one to form the diamond
shape you see in Figure 20-8. This shape is filled in and given a thick outline, thanks

to the Fill, Stroke, and StrokeThickness properties, which are available on any shape
element.
The shape defined by the Data describes
the center of the line drawn for
the outline. This means that making the StrokeThickness larger effec-
tively increases the size of the shape—a thicker outline will encroach
into the interior of the shape, but will also expand outward by the same
amount. That means that the Path in Example 20-10 has a bounding
box slightly larger than that implied by the coordinates in the Data. The
first line segment starts at (50, 0), which is at the very top of the shape,
but the stroke thickness means that the peak of the shape actually ap-
pears a bit higher. (The peak is at approximately (50, −3.54). The angle
of this particular stroke means that the top corner is above the specified
point by half the stroke thickness multiplied by √2.) So if you put this
path at the very top left of the UI its top and left corners will be slightly
cropped.
Figure 20-8. Path with straight edges
Elements and Controls | 749
Path offers more complex commands for drawing curved shapes. Example 20-11 shows
a shape with straight line segments and a single cubic Bezier curve segment, indicated
with the C command.
Example 20-11. Path with Bezier curve and straight edges
<Path Fill="Red" Stroke="Black"
StrokeThickness="5"
Data="M50,0 L100,50 C125,74 75,125 50,100 L0,50 z"
/>
Cubic Bezier curves require four points to define them. So the C command demands
three pairs of coordinates. (The first point is wherever the previous command finished,
so it requires three more to bring the total to four.) Therefore, in this case, the three
pairs of numbers that follow the C do not constitute three repeated commands as they

did with L. You can repeat the C command; you just need to add three pairs for each
segment. Figure 20-9 shows the shape defined by Example 20-11.
Figure 20-9. Path with a mixture of straight and curved edges
These examples
have used simple named colors for the Fill and Stroke, but you can
get more advanced. You can specify hexadecimal RGB colors using a #, as you would
with HTML—for example, Fill="#FF8800" indicates a shade of orange, by mixing full-
strength red (FF) with slightly more than half-strength green (88) and no blue (00). You
can extend this to eight digits to define partially transparent colors—for example,
Fill="8000FFFF" specifies an alpha (transparency) of 80 (semitransparent), 0 red, and
full-strength green and blue, to define a semitransparent shade of turquoise.
You can also create more complex brushes. Linear and radial gradient brushes are
available. Example 20-12 sets the fill of a shape to a radial gradient brush, and sets its
stroke to be a linear gradient brush.
Example 20-12. Gradient brushes for fill and stroke
<Path StrokeThickness="10"
Data="M50,0 L100,50 C125,74 75,125 50,100 L0,50 z"
>
<Path.Fill>
<RadialGradientBrush>
<GradientStop Offset="0" Color="Blue" />
<GradientStop Offset="1" Color="White" />
</RadialGradientBrush>
750 | Chapter 20: WPF and Silverlight
</Path.Fill>
<Path.Stroke>
<LinearGradientBrush StartPoint="0,0"
EndPoint="0,1">
<GradientStop Offset="0" Color="Black" />
<GradientStop Offset="0.5" Color="White" />

<GradientStop Offset="1" Color="Black" />
</LinearGradientBrush>
</Path.Stroke>
</Path>
As
you
can see in Figure 20-10, these brushes change color across the shape. The radial
brush starts from a point in the middle (or some other point—there are properties to
control the exact settings) and spreads out to an elliptical boundary. The linear gradient
brush simply changes colors between the specified start and end points. Notice that
you can run through as many different colors as you like with the GradientStop
elements.
Figure 20-10. Gradient brushes
You can
even create a bitmap-based brush with which to paint shapes, so let’s look at
bitmap handling next.
Images
The shape elements are great for graphics that can be built out of geometric elements.
Skilled designers can produce remarkably realistic-looking imagery with these sorts of
primitives using tools such as Adobe Illustrator. However, some kinds of pictures do
not lend themselves to this sort of construction—photographs, for example. You might
be able to draw a stylized rendition of a photograph, but if you just want to incorporate
a photographic image directly into an application, bitmaps are the way to go.
Bitmaps are pixel-oriented rather than vector-based. (From a tool perspective, it’s like
the distinction between Adobe Photoshop and Adobe Illustrator.) Bitmaps do not scale
as well—if you enlarge a bitmap, eventually you just see the individual pixels, leading
to an appearance that is either jagged or fuzzy, depending on the way in which the
bitmap is enlarged. Shapes don’t have that problem; because shapes are geometrically
defined, WPF or Silverlight can render them perfectly crisply no matter how large you
Elements and Controls | 751

make them. So there’s a trade-off here—bitmaps can offer a much more photorealistic
impression than vector art, but they don’t adapt so well to changes in size. That’s why
graphics systems need to support both.
The simplest way to use a bitmap is with the <Image> element. You can point its
Source property at any URL that contains a bitmap. Example 20-13 uses a miscellaneous
image from one of the authors’ blogs. WPF or Silverlight will download and display
the image at runtime. (The image may not appear in the design view, though.)
Example 20-13. Image element with HTTP URL
<Image Source=" /> Stretch="None" />
The Stretch property indicates how to size the image. The value None says that we want
the image to be rendered at its natural size. The Image element’s default behavior is to
resize the bitmap so that it fills the layout slot, but that’s not always appropriate. This
particular image happens to be a screenshot, and those tend to go a bit blurry if you
resize them, so disabling stretching is a good idea here. Resizing is less problematic for
photographs, though, so the default behavior of stretching to fit is useful there.
The Image class is a user interface element, deriving from FrameworkElement like any
other. But there’s also ImageBrush—this derives from a different class, Brush, in common
with the gradient brushes we saw earlier. You can use an ImageBrush to paint a shape.
Example 20-14 uses the same image to provide the Fill of a Path. (Again, you may find
that the image appears only at runtime, not at design time.)
Example 20-14. Painting a shape with an ImageBrush
<Path StrokeThickness="3" Stroke="Black"
Data="M50,0 L100,50 C125,74 75,125 50,100 L0,50 z"
>
<Path.Fill>
<ImageBrush
ImageSource=" /> />
</Path.Fill>
</Path>
You don’t have to download images with HTTP. You can compile an image into a WPF

or Silverlight application as a resource—simply adding a JPEG or PNG to the project
in Visual Studio will do that. Or with WPF you can point an Image or ImageBrush at a
file on disk.
752 | Chapter 20: WPF and Silverlight
Silverlight supports only JPEG and PNG bitmaps—to keep the Silver-
light plug-in download small, Microsoft chose a minimal set of formats,
and these two cover most bases. JPEG provides efficient compression
for photographic and photorealistic images, but does a bad job with
screenshots and doesn’t support transparency. Conversely, PNG can
reproduce screenshots perfectly and supports transparency, but com-
presses photographic images inefficiently.
WPF supports a much wider range of image types, including TIFF, BMP,
and GIF. Moreover, it’s built on top of the extensible Windows Imaging
Components (WIC) mechanism, so the set of supported formats is not
closed. Some digital camera vendors provide WIC drivers for their na-
tive raw image formats, so if you have those installed, WPF can display
those images directly.
Still images may not be enough for your application. You might want to incorporate
movies.
Media
WPF and Silverlight offer the MediaElement, which can render videos. It can also be
used to play audio files. In use, it’s almost identical to the Image element; you just point
it at a video file rather than a bitmap.
Silverlight offers a VideoBrush that lets you create a brush from a video, in the same way
that ImageBrush lets you create a brush from a bitmap. Slightly surprisingly, WPF does
not offer this type—this is a good example of how Silverlight is not a subset of WPF.
It’s possible to paint things with video in WPF, though; you just do it using something
called a VisualBrush. VisualBrush is far more powerful than VideoBrush—it lets you
take any UI element (even one that has children, like a panel) and turn it into a brush.
So you can wrap a MediaElement in a VisualBrush to create the same effect; Silverlight

doesn’t have VisualBrush, which is why it provides the more specialized VideoBrush.
Speaking of moving images, you can also apply movement to other elements in a user
interface.
Animation
WPF and Silverlight allow any element to be animated—most properties that have an
impact on the appearance of the UI can be modified over time. Of course, you could
achieve that yourself by setting up a timer, and modifying properties of UI elements
each time the timer fires. But you can let the animation system do that work for you.
A complete description of animation would fill a chapter, but Example 20-15 shows a
typical example.
Elements and Controls | 753
Example 20-15. An animation
<UserControl.Resources>
<Storyboard x:Key="ellipseAnimation">
<DoubleAnimation
From="50" To="100"
AutoReverse="True" RepeatBehavior="Forever"
Storyboard.TargetName="animatedEllipse"
Storyboard.TargetProperty="Width" />
</Storyboard>
</UserControl.Resources>
Animations
are separate objects from the things they animate, and typically live in a
Resources section—all elements have a Resources property which is a handy place to
put useful objects. It’s just a dictionary—a name/value collection—a specialized dic-
tionary similar to those of the kind described in Chapter 9. This particular example
would appear as a child of the UserControl at the root of the user interface.
While this is a simple example, it illustrates all the important points. The whole thing
is contained in a Storyboard—this is a collection of animations. Animations are always
defined in storyboards, as this enables you to target multiple properties, or perhaps

orchestrate a sequence of different animations over time. This example is simple and
contains just a single animation, but we’re still required to put it in a Storyboard.
The animation itself has a From and a To value specifying the range of values the property
will span during the animation—these are numbers because this is a DoubleAnimation
(as in the System.Double floating-point type); if this were a ColorAnimation you’d see
color values in there instead. The AutoReverse and RepeatBehavior properties here in-
dicate that this animation runs back and forth indefinitely. And the final two properties
indicate the element and property to be animated. So somewhere in the Xaml we’d
expect to find an element with the name indicated, for example:
<Ellipse x:Name="animatedEllipse"
Fill="Blue" />
Something needs to kick the animation off. In the code behind, you’d extract the ani-
mation from the resources and start it like this:
Storyboard anim = (Storyboard) Resources["ellipseAnimation"];
anim.Begin();
There are other ways to start animations. WPF supports triggers, which let you place
instructions in Xaml that certain animations should be run when specific things hap-
pen. So you could tie an animation to the raising of a MouseEnter event, for example,
or run an animation when the value of a property changes. You can do something
similar in Silverlight using behaviors, which make it easy to define a variety of UI re-
sponses (such as running animations) with Expression Blend. Both WPF and Silverlight
also support automatic running of animations in control templates, as we’ll see later.
754 | Chapter 20: WPF and Silverlight
3D graphics
WPF has basic support for 3D graphics, but that’s a topic that would take a whole
chapter to cover in itself, so we won’t be getting into that in this book. Silverlight doesn’t
have WPF’s 3D features, but it does have some very limited support for 3D in the form
of special transforms. Besides the RenderTransform we saw earlier, you can set an ele-
ment’s Projection property to make it look like it has been rotated in 3D, including
perspective effects you can’t get with a 2D affine transform. This falls short of the full

3D models you can create in WPF, but provides the bare bones required to build up
3D aspects to the user interface.
Layout and graphical services are necessary to render things on-screen, but most ap-
plications require something a little more high-level—standard elements the user can
interact with. So WPF and Silverlight provide controls.
Controls
Silverlight and WPF offer a range of controls, similar to many of the common controls
you find in typical Windows applications. For example, there are buttons—CheckBox
and RadioButton for selection, Button for a basic pushbutton, and HyperlinkButton for
when you want to make your button look like a hyperlink. There’s also RepeatButton,
which looks like a normal button but repeatedly raises click events for as long as you
hold the button down.
For the most part, these work in a very straightforward fashion—you already saw how
to handle the Click event, in Example 20-2 and Example 20-3. And as you’d expect,
the two selection buttons offer events called Checked and Unchecked to notify you when
they’re toggled, and an IsChecked property to represent the state. However, there is one
potentially surprising feature that buttons inherit from their ContentControl base class.
Content controls
Many controls have some sort of caption—buttons usually contain text; tab pages have
a header label. You might expect these controls to offer a property of type string to
hold that caption, but if you look at the Content property of a Button or the Header of
a TabItem, you’ll see that these properties are of type object. You can put text in there,
but you don’t have to. Example 20-16 shows an alternative.
Example 20-16. Button with Ellipse as content
<Button>
<Button.Content>
<Ellipse Fill="Green" Width="100" Height="50" />
</Button.Content>
</Button>
Elements and Controls | 755

In fact, you don’t need to write that <Button.Content> property element—the base
ContentControl class is marked with a [ContentProperty("Content")] attribute, which
tells the Xaml compiler to treat elements that appear inside the element as the value of
the Content property. So Example 20-16 is equivalent to this:
<Button>
<Ellipse Fill="Green" Width="100" Height="50" />
</Button>
This creates a button with a green ellipse as its content. Or you can get more ambitious
and put a panel in there:
<Button>
<StackPanel Orientation="Horizontal">
<Ellipse Fill="Green" Width="100" Height="50" />
<TextBlock Text="Click me!" FontSize="45" />
<Ellipse Fill="Green" Width="100" Height="50" />
</StackPanel>
</Button>
Figure 20-11 shows the results. Content controls let you go completely crazy—there’s
nothing stopping you from putting buttons inside buttons inside tab controls inside
listbox items inside more buttons. Just because you can doesn’t mean you should, of
course—this would be a terrible design for a user interface. The point is that you’re
free to put any kind of content in a content control.
Figure 20-11. Button with mixed content
Some controls
can contain multiple pieces of content. For example, a TabItem has a
Content property which holds the main body of the tab page, and also a Header property
for the tab caption. Both properties accept any kind of content. And then the items
controls take this a step further.
Items controls
ItemsControl is the base class of controls that display multiple items, such as ListBox,
ComboBox, and TreeView. If you add children to these controls, each child can be an

arbitrary piece of content, much like a button’s content but with as many children as
you like. Example 20-17 adds various elements to a ListBox.
Example 20-17. ListBox with mixed content
<ListBox>
<StackPanel Orientation="Horizontal">
756 | Chapter 20: WPF and Silverlight
<Ellipse Fill="Green" Width="100" Height="50" />
<TextBlock Text="Text and graphics" FontSize="45" />
<Ellipse Fill="Green" Width="100" Height="50" />
</StackPanel>
<Button Content="Button" />
<TextBox Text="Editable" />
</ListBox>
Figure
20-12
shows the results. As well as showing the content we provided, the
ListBox provides the usual visual responses to mouse input—the item underneath the
mouse has a slightly darker background than the item below to indicate that it can be
selected. The item at the bottom is darker still because it is currently selected. These
highlights come from the item container—all items controls generate an item container
for each child. A ListBox will generate ListBoxItem containers; TreeView generates
TreeViewItem objects, and so on.
Figure 20-12. ListBox with mixed content
Sometimes it’s useful to bring your own container, because you may need to do more
than populate it with a single piece of content. For example, when building a tree view,
you don’t just need to set the node caption; you may also want to add child nodes.
Example 20-18 explicitly creates TreeViewItem containers to define a tree structure.
Example 20-18. Explicit TreeViewItem containers
<ctl:TreeView>
<ctl:TreeViewItem>

<ctl:TreeViewItem.Header>
<StackPanel Orientation="Horizontal">
<Ellipse Fill="Green" Width="100" Height="50" />
<TextBlock Text="Content" FontSize="45" />
<Ellipse Fill="Green" Width="100" Height="50" />
</StackPanel>
</ctl:TreeViewItem.Header>
<ctl:TreeViewItem Header="Child A" />
<ctl:TreeViewItem Header="Child B" />
</ctl:TreeViewItem>
Elements and Controls | 757
<ctl:TreeViewItem>
<ctl:TreeViewItem.Header>
<Button Content="Button" />
</ctl:TreeViewItem.Header>
<ctl:TreeViewItem Header="Child 1" />
<ctl:TreeViewItem Header="Child 2" />
<ctl:TreeViewItem>
<ctl:TreeViewItem.Header>
<Button Content="Child 3" />
</ctl:TreeViewItem.Header>
</ctl:TreeViewItem>
</ctl:TreeViewItem>
<ctl:TreeViewItem>
<ctl:TreeViewItem.Header>
<TextBox Text="Editable" />
</ctl:TreeViewItem.Header>
</ctl:TreeViewItem>
</ctl:TreeView>
Notice the unusual ctl: prefix—see the sidebar on the next page for an explanation.

As
you
can see from Figure 20-13, each Header property value has ended up as the label
for a single node in the tree. The parent-child relationship of the nodes is determined
by the nesting of the TreeViewItem elements in the Xaml.
Figure 20-13. TreeView with content
758 | Chapter 20: WPF and Silverlight
Control Libraries and Xaml
Example 20-18 uses the TreeView control and its associated TreeViewItem container.
These are not built into the main Silverlight plug-in. They are provided as part of the
Silverlight SDK in a separate DLL called System.Windows.Controls, which ends up get-
ting built into your Silverlight application. Unlike normal .NET applications, Silverlight
applications are packaged into a ZIP file (usually given a file extension of .xap, which
is pronounced “zap”) so that multiple components and resources can be bundled into
a single application. This file must include any control libraries—either those provided
by Microsoft or third parties, or ones you’ve written.
To build a DLL into your Silverlight application package, you just add a reference to
the DLL in Visual Studio in the usual way.
When using controls from libraries, you need to let the Xaml compiler know where it’s
supposed to find the control. So for Example 20-18 to work, something extra needs to
go in the Xaml. The root element would contain an extra XML namespace declaration:
xmlns:ctl="clr-namespace:System.Windows.Controls;
assembly=System.Windows.Controls"
(That would normally be on one long line with no spaces—it has been split to fit on
the page.)
This means that anytime we use an element whose name starts with the ctl: prefix,
we’re using a type defined in the System.Windows.Controls namespace, in the
System.Windows.Controls DLL (or assembly, as .NET calls DLLs and EXEs).
While WPF uses the same XML namespace mechanism for control libraries, the
TreeView is built into the main .NET Framework. So you can use it like any other ele-

ment, and you don’t need to add extra DLLs or XML namespace prefixes.
Microsoft provides a suite of extra controls for Silverlight in the Silverlight Toolkit,
available from at />you’ll find the WPF Toolkit, which offers some additional controls for WPF.
While you can add elements directly to items controls like this, it’s often easier and
more flexible to use data binding, so we’ll be coming back to items controls later.
Because this
chapter is just an introduction to Silverlight and WPF, we won’t go through
all the available controls in detail. There are simple data entry controls such as TextBox,
AutoCompleteBox, Slider, and DatePicker. There are more comprehensive data-oriented
controls such as DataGrid and DataPager. There are also utility controls such as the
draggable Thumb and GridSplitter. But there’s one more kind of control we need to
look at: user controls.
Elements and Controls | 759
User Controls
A user control is, as the name suggests, a user-defined control. In Silverlight, you’ll have
at least one of these—your whole user interface is one big user control, as you can see
from the <UserControl> element at the root of your main page’s Xaml. But you can
create more. User controls are a useful way to manage complexity.
A problem that crops up a lot in big WPF and Silverlight projects—particularly the first
such project any team works on—is the 10,000-line Xaml file. Visual Studio creates
one Xaml file for your user interface, and the path of least resistance is to put everything
in there. As you add graphical resources, templates, data sources, animations, styles,
and all the other things you can put in Xaml, it can grow very large surprisingly quickly.
And there’s a related problem of having the entire application’s functionality in the one
code behind file. Such programs are not maintainable, so you need to split things up.
Instead of creating one big Xaml file, it’s usually best to try to have as little as possible
in your main page. It should typically do nothing more than define the overall layout,
saying where each piece of the UI belongs. And then each part can go into its own user
control. A user control is simply a Xaml file with some code behind. And since Xaml
files with code behind always compile into classes, you can use them from other Xaml

files—remember that Xaml is just a way to create objects. Example 20-19 shows the
Xaml for an application’s main UI that uses this approach.
Example 20-19. Main UI containing nothing but user controls
<UserControl
x:Class="SlUcExample.MainPage"
xmlns=" /> xmlns:x=" /> xmlns:app="clr-namespace:SlUcExample"
xmlns:d=" /> xmlns:mc=" /> mc:Ignorable="d" d:DesignWidth="640" d:DesignHeight="480">
<Grid x:Name="LayoutRoot">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<app:SearchBarView Grid.Column="0" Grid.ColumnSpan="2" />
<app:ProductListView Grid.Column="0" Grid.Row="1" />
<app:ProductDetailsView Grid.Column="1" Grid.Row="1" />
</Grid>
</UserControl>
760 | Chapter 20: WPF and Silverlight
Notice that this example defines an XML namespace prefix, app, and tells the Xaml
compiler that this refers to types in the SlUcExample namespace—the default project
namespace for this particular example. This time we don’t need the assembly= part
because the user controls are defined as part of this project, not in a separate DLL. This
prefix then refers to three user controls which would be defined elsewhere in the project.
Defining the user controls themselves is simple. You can add them as new items to your
project in Visual Studio, and it will create a Xaml file with a corresponding code behind
file, which you edit in exactly the same way as the main UI.

As you can see in Example 20-19, we chose names that end in View for
all the user controls. This is not mandatory, but it helps distinguish user
control classes, which define appearance and superficial interactive be-
havior, from the other types that define the core behavior of your ap-
plication. This distinction isn’t useful if you plan to put everything into
the code behind, of course, but we presume you have more refined soft-
ware design sensibilities than that, and will want to ensure that each
class in your application has a single, well-defined, reasonably narrow
responsibility.
User controls can contain any other controls and elements, so you can use elements
built into Silverlight as well as any control libraries you may have acquired. So user
controls have a lot of flexibility. However, you don’t necessarily have to build a user
control anytime you want some custom UI—the scope for customization of built-in
controls is greater than you might think, thanks to control templates.
Control Templates
As you already saw, controls are elements that have interactive behavior of some kind—
buttons are clickable; you can type into text boxes; you can scroll through the items in
a listbox and select them. What may not be obvious is that most controls only provide
behavior. Controls do not define their own appearance.
This may appear to be a ludicrous claim. After all, if you add a Button to your user
interface, you can see it. In fact, the appearance comes from a separate entity called a
template. Controls have a default template, which is why something appears when you
create a control, but this separation of appearance from behavior is important because
you are free to replace the default template with your own. This lets you change the
appearance of a control completely, without losing any of the behavior.
The behavior of controls is often surprisingly subtle and complex. You might think that
a button is a pretty simple sort of thing, and that you could create your own equivalent
by handling the MouseLeftButtonDown event on a shape. And while that would give you
a clickable element, there’s a lot missing. For example, there’s the way buttons push
down and pop back up. They should respond to keyboard input as well as mouse input.

Control Templates | 761
They should be visible to accessibility tools so that users with visual or coordination
issues can use your application. And a button is about as simple as it gets. If you’ve
ever used a Flash application with, say, a scroll bar that just didn’t feel like it was
working properly you’re already familiar with the hazards of trying to recreate basic
controls from scratch. Fortunately, control templates mean you don’t have to.
Only controls have templates. So while types such as Button and
TextBox have them, more primitive types such as shapes and TextBlock
—UI elements that don’t have any intrinsic behavior—don’t. This
shouldn’t be too surprising; an Ellipse element’s only job is to look like
an Ellipse, so what would it mean for it to have a template? (And what
element would you use inside the template to define the appearance?
Another Ellipse? Where would it get its appearance from?)
The Control base class defines a Template property. To customize the appearance of a
control, you simply set this property. As Example 20-20 shows, the property expects a
ControlTemplate object, and then inside this, you can put any element you like to define
the appearance. (You could, of course, use a panel if you wanted to build up a complex
appearance with multiple elements.)
Example 20-20. Button with custom template
<Button Content="OK" FontSize="20">
<Button.Template>
<ControlTemplate TargetType="Button">
<Border
Background="LightBlue"
BorderThickness="3"
BorderBrush="Black"
CornerRadius="10">
<ContentPresenter
Margin="20"
Content="{TemplateBinding Content}"

HorizontalAlignment="Center"
VerticalAlignment="Center"
/>
</Border>
</ControlTemplate>
</Button.Template>
</Button>
Figure 20-14 shows the results. It’s rather static—it doesn’t offer a visual response to
mouse activity yet, but we’ll fix that later. But it will still raise the Click event when
clicked, so it’s functional, if rather dull. Notice that we’ve set the Content property of
the button, and this content—the text “OK”—has appeared as you’d hope. That
doesn’t happen automatically; our template needs to say where the content should
appear, and that’s the purpose of the ContentPresenter in Example 20-20. Templates
762 | Chapter 20: WPF and Silverlight
for content controls need one of these placeholders for the Content property to do
anything. And if you’re defining a template for a control that can hold multiple pieces
of content—the Content and Header of a TabItem, for example—you need to provide a
ContentPresenter for each.
Figure 20-14. Button with custom template
How does
Silverlight (or WPF) know which placeholder corresponds to which prop-
erty? Look at the Content property of the ContentPresenter in Example 20-20—its value
has an unusual syntax. The attribute value is enclosed in braces, which indicates that
we’re not setting a literal value—in this case the TemplateBinding text signifies that we
want to connect this particular property in this element in the template to a corre-
sponding property of this template’s control. So {TemplateBinding Content} connects
this ContentPresenter to our Button element’s Content property, while {TemplateBind
ing Header} would connect it to the Header property in a control that had such a
property.
In fact, it’s common to use many template bindings. Example 20-20 hardcodes a lot of

features of the appearance into the template, but it’s possible to reuse templates on
several different controls, at which point you might want to retain the flexibility to
change things such as the background color, border thickness, and so on, without
needing to define a new template every time. Example 20-21 looks the same as Fig-
ure 20-14, but instead of hardcoding everything into the template it picks up more of
the control’s properties using template bindings.
Example 20-21. Template with less hardcoding
<Button Content="OK" Background="LightBlue">
<Button.Template>
<ControlTemplate TargetType="Button">
<Border
Background="{TemplateBinding Background}"
BorderThickness="{TemplateBinding BorderThickness}"
BorderBrush="{TemplateBinding BorderBrush}"
CornerRadius="10">
<ContentPresenter
Margin="{TemplateBinding Padding}"
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding HorizontalContentAlignment}"
/>
Control Templates | 763
</Border>
</ControlTemplate>
</Button.Template>
</Button>
This
template
is now looking like a candidate for reuse—we might want to apply this

to lots of different buttons. The usual way to do this is to wrap it in a style.
Styles
A style is an object that defines a set of property values for a particular type of element.
Since elements’ appearances are defined entirely by their properties—Template is a
property, remember—this means a style can define as much of a control’s appearance
as you like. It could be as simple as just setting some basic properties such as
FontFamily and Background, or it could go as far as defining a template along with prop-
erty values for every property that affects appearance. Example 20-22 sits between these
two extremes—it puts the template from Example 20-21 into a style, along with settings
for a few other properties.
Example 20-22. Button style
<UserControl.Resources>
<Style x:Key="buttonStyle" TargetType="Button">
<Setter Property="Background" Value="LightBlue" />
<Setter Property="BorderBrush" Value="DarkBlue" />
<Setter Property="BorderThickness" Value="3" />
<Setter Property="FontSize" Value="20" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border
Background="{TemplateBinding Background}"
BorderThickness="{TemplateBinding BorderThickness}"
BorderBrush="{TemplateBinding BorderBrush}"
CornerRadius="10">
<ContentPresenter
Margin="{TemplateBinding Padding}"
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"

VerticalAlignment="{TemplateBinding HorizontalContentAlignment}"
/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
764 | Chapter 20: WPF and Silverlight

×