Chapter 8. Getting a grip on graphics – Silverlight 2 in Action, illustrated edition

Chapter 8. Getting a grip on graphics

This chapter covers

Silverlight’s extensive multimedia support enables you to create internet-based experiences guaranteed to tingle your users’ senses. With the fruits of audio and video baked into the Silverlight pie, you can serve up true multimedia sweetness. And, with the native raster-based image support and Deep Zoom providing the whipped topping, Silverlight empowers you to dish out an interactive dessert that demands attention.

Another way to capture your users’ attention is with Silverlight’s commanding graphics engine. Everyone has heard the timeless adage a picture is worth a thousand words. By wielding the powerful graphical capabilities within Silverlight, you can deliver immersive illustrations that will spawn more than a thousand words. These joyful expressions will come from users who have emerged from a fog of confusion that was lifted by an illuminating illustration.

In addition to helping individuals connect with difficult concepts, illustrations make it easier for individuals to digest large amounts of data. Envision keeping an exhaustive statistical digest of your favorite baseball team over the course of a season. This amount of data could easily span several reams worth of paper.

When it comes down to it and you need to decide whether or not to trade a player in your fantasy league, you don’t want to have to sort through all this information. You want to be able to glance at a player’s stats and quickly make your decision. This type of scenario is regularly solved through the use of a dashboard, which is used to cleanly summarize information. Dashboards regularly incorporate illustrations, such as charts and graphs, to effectively condense large amounts of data.

Silverlight brings one more dessert to the visual picnic. Graphics within Silverlight are vector-based; they’re mathematically based objects. They’re ideal for internet distribution because vector-based graphics can be condensed to a smaller file size than their raster counterparts. In addition, your visual elements can scale up and down without a loss of resolution.

Vector-based graphics are more than eye candy—they’re an extension to accessibility. In traditional application environments, users with diminished eyesight generally have to squint to absorb visual content such as text and icons. Through scalability, these same users can fully enjoy your application with ease.

Throughout this chapter, you’ll see the expanse of graphical capabilities within Silverlight. This pilgrimage will begin by discussing the most primitive shapes such as lines, rectangles, and ellipses. After discussing the concept of geometries, we’ll lead you down a new path and show you how to paint shapes and alter the way in which they’re rendered.

8.1. Getting in shape

Shapes are probably the most regularly used elements when creating an illustration because a Shape is the common basis for the Line, Rectangle, and other Shape elements, which you’ll see shortly. Each Shape is painted by two fundamental Brush elements. (Brushes are discussed later.) The first Brush, called Stroke, defines the outline of a Shape. The second Brush, called Fill, describes how everything inside the boundary of the Shape should be painted. It is possible to create a Shape without specifying the Stroke and Fill properties; but, if you don’t specify the Stroke or Fill, you’ll basically paint an invisible shape.

Throughout this section, we’ll build on the concept of an abstract Shape to create concrete visual elements. A lot of these visual elements will resemble shapes you learned on Sesame Street, and some of these shapes will be a bit more complex. Table 8.1 provides a list of the shapes we’ll discuss.

Table 8.1. The Shape objects available within Silverlight.

Element

Description

Line A thin, continuous mark that connects two points.
Ellipse In layman’s terms, a circle that can be stretched vertically or horizontally.
Path A collection of connected curves and lines.
Polygon A series of connected lines that make a closed shape.
Polyline A series of connected straight lines
Rectangle A four-sided plane with four corners. i.e.: □

The following sections describe each Shape listed in the table in greater detail. The shapes are described progressively in relation to complexity. The Path element is part of a more general category that will be covered later in this chapter. First, you’ll learn about the most rudimentary shape, the Line.

8.1.1. A Line in the sand

A Line is, obviously, a continuous line that connects two end points. Snippet 8.1 shows a basic line between two points and the XAML used to define it.

Snippet 8.1. XAML Result: A basic Line

<Canvas x:Name="myCanvas" Height="20" Width="50">
<Line Stroke="Black" X1="10" Y1="10" X2="30" Y2="30" />
</Canvas>

There are four double-precision floating-point properties (X1, Y1, X2, Y2) that specify the x-and-y coordinate pairs that define the beginning and ending points of the Line. Without these properties, your Line will be little more than a figment of your imagination.

Interestingly, these coordinates don’t represent an absolute position. They specify a relative position within the coordinate space of the containing layout panel. Note that, although Silverlight won’t automatically define the endpoints of a Line, the coordinate space of the containing layout panel may be automatically created. Regardless, the values of the coordinates represent pixel values, whether absolute or relative positioning is used.

The Canvas used in snippet 8.1 has a specific area. But as described in chapter 3, some layout panels provide a more dynamic layout environment. For instance, if this Line were the second element defined within a StackPanel, it could end up in a potentially undesired location because the coordinates within a Line element specify a relative position.

8.1.2. Rectangle

A Rectangle does exactly what its name implies—it defines a rectangle. The Rectangle within Silverlight provides one interesting tidbit that we’ll expose after snippet 8.2, which shows the basic syntax of a Rectangle.

Snippet 8.2. XAML Result:A basic Rectangle

<Rectangle Stroke="Black" Width="104" Height="64"
Canvas.Left="8" Canvas.Top="8"/>

This snippet shows an archetypal Rectangle. The key properties involved in the definition of the element are Width and Height. Collectively, these double properties assist in creating the boundary of the Rectangle. You can determine the area of the Shape by multiplying these two property values. (This nostalgic mathematical fact isn’t the interesting tidbit alluded to earlier.)

The Rectangle element exposes two properties, RadiusX and RadiusY, which empower you to easily round off the corners of any Rectangle. Before you see an example of this, consider how difficult this task would be within traditional HTML. Although there are several options, the most straightforward involves importing an image. Now examine the XAML in snippet 8.3, and notice how simple it is to implement this elegant feature.

Snippet 8.3. XAML Result: A Rectangle with rounded corners

<Rectangle Stroke="Black" Width="104" Height="64"
Canvas.Left="8" Canvas.Top="8" RadiusX="10" RadiusY="10"/>

The RadiusX and RadiusY double-precision floating-point properties allow you to set the radius of the ellipse used to round off the corners of the Rectangle. You’ll see the Ellipse element in two shakes of a pup’s tail. It’s fun to see that, by lopsidedly setting the RadiusX and RadiusY properties, you can give a Rectangle a bulging look as shown in snippet 8.4.

Snippet 8.4. XAML Result: A bulging rectangle

<Rectangle Stroke="Black" Width="104" Height="64"
Canvas.Left="8" Canvas.Top="8" RadiusX="15" RadiusY="50"/>

The bulging Rectangle is a fun little option. But occasionally, you may need a fully rounded shape. This is where the Ellipse comes into play.

8.1.3. The ellipse

An Ellipse defines a basic circular shape. Snippet 8.5 shows a basic Ellipse and the XAML used to define it.

Snippet 8.5. XAML Result: The syntax and look of a basic Ellipse

<Ellipse Stroke="Black" Width="104" Height="64"
Canvas.Left="8" Canvas.Top="8"/>

The Ellipse doesn’t provide any properties that distinguish it from the Rectangle. The difference lies in how the two Shape elements are rendered. It’s important to recognize that Silverlight provides this type of Shape for your graphical needs—if for nothing else than to know that you can draw a circle. Now, let’s move onto something a little more interesting: the Polyline.

8.1.4. Polyline

What if you need to create an application that represents an EKG (or ECG) monitor? How would you go about displaying the electrical impulses projected by a heart? Or, perhaps you need to create a line chart that represents sales or financial trends. These types of scenarios can entail large amounts of data that can be best illustrated through intricate line art drawings.

You could use several Line elements, but this could prove to be slightly cumbersome. The Polyline provides a nice alternative that allows you to create a series of connected line pieces using a single element. Snippet 8.6 shows a Polyline in action.

Snippet 8.6. XAML Result: A Polyline

<Polyline Stroke="Black"
Points="10,50 20,40 23,44 25,49 40,12 46,50 51,42 55,50" />

The Polyline uses a space-delimited list of coordinate pairings to define the line drawn. Although each coordinate pair in this snippet contains integer values, each value represents a Point. A Point is represented in the form of “[X-Coordinate],[Y-Coordinate]”. Collectively, all these Point elements are stored within the Points property. In being consistent with the Line, each Point within the list is relative to the containing layout panel.

8.1.5. Polygon

The Polygon goes one step beyond the Polyline by ensuring the Shape is always closed. A Polyline creates an open Shape, whereas a Polygon always draws a closed Shape. Snippet 8.7 shows a basic trapezoid created with a Polygon.

Snippet 8.7. XAML Result: A basic Polygon in action

<Polygon Stroke="Black" Points="10,40 20,10 60,10 70,40 10,40" />

Like the sibling Polyline, the Polygon also utilizes the Points property. This property works in a manner similar to the Points property of the Polyline; but, regardless of your selected coordinates, the Polygon will always draw a closed shape.

Snippet 8.8 shows a Polyline and a Polygon using the same coordinates to illustrate how Silverlight renders them.

Snippet 8.8. XAML Result Type: An open shape compared to a closed shape

The available shapes provide a lot of flexibility to give your users valuable graphical experiences. Occasionally, your requirements may exhaust the abilities of the various shapes. A Geometry is a much more versatile option that can address the inadequacies of a Shape.

8.2. Geometry

At first, a Geometry seems similar to a Shape because they both describe 2D shapes. Unlike Shape elements, Geometry objects are not UIElement entities. UIElement objects have an intrinsic ability to render themselves and expose graphical properties, such as Opacity, that Geometry objects don’t have. Why, then, would you consider using a Geometry? Well, a Geometry allows you to do the following:

  • Define a geometric shape. For example, imagine creating a user-based rating system. In this scenario, you may wish to use a set of five-pointed stars to rate an item. Although a star isn’t a predefined shape, you could create this element using a Geometry.
  • Define a region for clipping. Clipping is used to limit the visible area of another object.
  • Define a region that can be used for hit-testing.

These compelling reasons make examining the Geometry object a worthwhile endeavor. A Geometry is really just an abstract concept. In fact, you can’t deliberately create just a Geometry. Instead, you must rely on the geometrical concepts spread across three basic categories: simple, path, and composite geometries.

8.2.1. Simple geometries

A simple geometry reflects some of the primitive geometrical shapes that you’ve already seen. Simple geometries—such as the LineGeometry, RectangleGeometry, and EllipseGeometry —are provided to help you illustrate lines, rectangles, and circles.

A LineGeometry illustrates the geometry of a basic line. Snippet 8.9 shows how to draw a line using a LineGeometry element. The example also shows what the same illustration would look like if you used the basic Line Shape described earlier.

Snippet 8.9. Line XAML Path XAML Result: Comparison between the Line and LineGeometry

<Path Stroke="Black" StrokeThickness="1" >
<Path.Data>
<LineGeometry StartPoint="8,8" EndPoint="72,72" />
</Path.Data>
</Path>

<Line X1="8" Y1="8" X2="72" Y2="72"
StrokeThickness="1" Stroke="Black" />

From this snippet, you can see that using the Line Shape XAML is much more compact. But you can use also Geometry objects for clipping and hit-testing.

In addition to the LineGeometry, a RectangleGeometry is also provided. The RectangleGeometry defines the geometry of a rectangle. Snippet 8.10 shows how to create a rectangle using a RectangleGeometry and also provides the corresponding definition with the Rectangle Shape.

Snippet 8.10. Rectangle XAML Path XAML Result: A RectangleGeometry compared to a Rectangle

<Path Fill="Navy" Stroke="Black" StrokeThickness="1">
<Path.Data>
<RectangleGeometry Rect="8,8,64,64" />
</Path.Data>
</Path>


<Rectangle Stroke="Black" StrokeThickness="1" Height="64"
Width="64" Canvas.Top="8" Canvas.Left="8" Fill="Navy">
</Rectangle>

Like the Rectangle Shape, the RectangleGeometry also supports corner-rounding via the RadiusX and RadiusY properties. Finally we’ll review the EllipseGeometry for the sake of completeness (snippet 8.11).

Snippet 8.11. Elipse XAML Path XAML Result: An EllipseGeometry compared to an Ellipse

<Path Fill="Navy" Stroke="Black" StrokeThickness="1">
<Path.Data>
<EllipseGeometry Center="40,40" RadiusX="36" RadiusY="36" />
</Path.Data>
</Path>

<Ellipse Canvas.Left="4" Canvas.Top="4" Height="72" Width="72"
Fill="Navy" StrokeThickness="1" Stroke="Black" />

As useful as lines, rectangles, and circles are, occasionally, you need to create a more dynamic shape. To create more complex shapes, Silverlight supports the use of the PathGeometry.

8.2.2. Path geometries

A PathGeometry empowers you to construct complex, detailed illustrations composed of a variety of arcs, curves, and lines. These intricate depictions consist of a collection of PathFigure objects with each PathFigure representing a small section of the overall illustration. In turn, each PathFigure is compiled by a series of PathSegment objects. Each PathSegment object describes a small piece of the overall figure. Before we get too far ahead of ourselves, let’s review a basic example that shows a variety of meaningless squiggly lines for the sake of illustration (snippet 8.12).

Snippet 8.12. XAML Result: A PathGeometry

<Canvas
Width="100" Height="100" Background="Gray">
<Path Stroke="Red" StrokeThickness="2">
<Path.Data>
<PathGeometry>
<PathGeometry.Figures>
<PathFigure StartPoint="5,5">
<PathFigure.Segments>
<ArcSegment Size="10,10" RotationAngle="30"
Point="20,10" IsLargeArc="False" SweepDirection="Clockwise" />
<BezierSegment Point1="40,0" Point2="60,60" Point3="75,90"/>
<LineSegment Point="80,15" />
<PolyLineSegment Points="50,90 3,7" />
<QuadraticBezierSegment Point1="90,90" Point2="70,60"/>
</PathFigure.Segments>
</PathFigure>
</PathGeometry.Figures>
</PathGeometry>
</Path.Data>
</Path>
</Canvas>

This snippet uses five different segment types to create random squiggles. Each individual segment sequentially connects to the previous, much like cars in a freight train. Table 8.2 shows all the available segment types.

Table 8.2. The list of available segment types.

Segment Type

Usage

LineSegment A straight line connecting two points.
PolyLineSegment A series of lines.
ArcSegment An elliptical arch between two points.
BezierSegment A cubic Bezier curve between two points.
PolyBezierSegment A series of cubic Bezier curves.
QuadraticBezierSegment A quadratic Bezier curve.
PolyQuadraticBezierSegment A series of quadratic Bezier curves.

From the options presented, it’s clear to see that you have tons of flexibility when it comes to creating a geometrical shape. Sometimes you may need to explicitly use other geometry objects. In these scenarios, you can use a composite geometry.

8.2.3. Composite geometries

You may need to create a complex shape that consists of disconnected entities. Or, you may need to utilize Geometry entities, and you want to combine their area. The GeometryGroup adequately addresses these scenarios. A GeometryGroup is a collection of Geometry entities. Snippet 8.13 illustrates how to orchestrate a composite geometry.

Snippet 8.13. XAML Result: A composite geometry to make a key

<Path Stroke="Navy" StrokeThickness="8" Fill="Navy">
<Path.Data>
<GeometryGroup FillRule="Evenodd">
<EllipseGeometry Center="20,40" RadiusX="15" RadiusY="15" />
<LineGeometry StartPoint="20,40" EndPoint="70,40" />
<LineGeometry StartPoint="66,38" EndPoint="66,55" />
<LineGeometry StartPoint="55,38" EndPoint="55,55" />
<EllipseGeometry Center="14,40" RadiusX="8" RadiusY="8" />
</GeometryGroup>
</Path.Data>
</Path>

This snippet illustrates how to create a key using a complex geometry via the GeometryGroup. It also introduces a property called FillRule, which determines how conflicting areas should be filled. There are two acceptable values: EvenOdd and Nonzero.

EvenOdd, the default used in the previous example, is pretty simple. EvenOdd begins at a point and goes outside of the overall shape, counting each line that it intersects along the way. If the count is odd, the point is inside the shape. If the count is even, the point is outside the shape. This rule determines how to fill the area.

Alternatively, if the previous example had used the Nonzero option, the hole to place the key on a key ring would have been filled because the Nonzero counts the number of lines it intersects along the way. But it also considers the direction of the line. Based on the direction, the count is either incremented or decremented. At the end of counting, if the total is zero, it’s assumed that the point is inside the overall shape. To really take control of how an element is filled, you can use one of Silverlight’s many brushes.

8.3. Brushes

Up to this point, you’ve seen how to define the boundaries of the various Shape elements. It’s equally important to understand how to fill the area within a Shape. To paint the interior of a Shape or a variety of other visual elements, you must choose from myriad Brush options including SolidColorBrush, LinearGradientBrush, RadialGradientBrush, ImageBrush, and VideoBrush.

8.3.1. SolidColorBrush

The SolidColorBrush is without a doubt the most rudimentary of the Brush options. A SolidColorBrush uses a single, solid color to paint an area. Snippet 8.14 shows a basic circle using a SolidColorBrush.

Snippet 8.14. XAML Result: A basic SolidColorBrush in action

<Ellipse Stroke="Black" StrokeThickness="3"
Width="64" Height="64" Canvas.Left="8" Canvas.Top="8">
<Ellipse.Fill>
<SolidColorBrush Color="Navy" />
</Ellipse.Fill>
</Ellipse>

This SolidColorBrush uses a System.Windows.Media.Color property named Color to specify which color will fill the area. Properties of this type can accept values represented in one of the following ways:

  • A predefined named color, such as Navy, that matches one of the names supported within Internet Explorer, .NET Framework, and Windows Forms. Importantly, the Color class in Silverlight belongs to the System.Windows.Media namespace. In Windows Forms, it belongs to the System. Drawing namespace.
  • A Red, Green, Blue (RGB) hexadecimal string in the format of #RxGyBz. For instance, in snippet 8.14, you could replace Navy with its hexadecimal representation, "#000080".
  • An RGB hexadecimal string with an alpha channel in the format of #aRGB. This format gives you a greater range than the typical RGB hexadecimal string because it has built-in support for the opacity channel. As an example, you could convert Navy to "#AA000080" to give the color a washed-out appearance.

These color options give you a lot of flexibility when you’re defining a SolidColor-Brush. If you’re using XAML, it’s much more convenient to explicitly set the Fill property of a Shape, or any property that’s a Brush, and let Silverlight automatically convert the value to a SolidColorBrush for you. Because of this, you could condense the previous snippet to that shown in snippet 8.15.

Snippet 8.15. XAML: The alternative approach to the SolidColorBrush
<Ellipse Stroke="Black" StrokeThickness="3" Fill="Navy"
Width="64" Height="64" Canvas.Left="8" Canvas.Top="8">
</Ellipse>

Although this explicit approach is convenient, it’s still important to remember the SolidColorBrush because, if you’re trying to use solid colors through managed code, you’ll need to use the System.Windows.Media. SolidColorBrush class. Occasionally, you may want something richer and more vibrant than a solid color. Thankfully, Silverlight provides several alternatives such as the LinearGradientBrush.

8.3.2. LinearGradientBrush

The LinearGradientBrush paints an area with a gradual, soothing shift between colors along a theoretical line. This Brush can shift between one or more colors through the use of a series of predefined locations represented as GradientStop elements. Each GradientStop element specifies where one color should shift to another. Snippet 8.16 shows a basic LinearGradientBrush that uses two GradientStop elements to shift from one Color to another.

Snippet 8.16. XAML Result: A basic LinearGradientBrush rendered diagonally

<Ellipse Stroke="Black" StrokeThickness="3"
Width="64" Height="64" Canvas.Left="8" Canvas.Top="8">
<Ellipse.Fill>
<LinearGradientBrush>
<GradientStop Color="Navy" Offset="0" />
<GradientStop Color="White" Offset="1" />
</LinearGradientBrush>
</Ellipse.Fill>
</Ellipse>

This Ellipse illustrates how the LinearGradientBrush can be used to shift from Navy in the upper-left corner to White in the lower-right corner. Each GradientStop within the LinearGradientBrush specifies an Offset property that determines where the color, specified in the Color property, should be reached within the Brush coordinate space. But how does the Offset property know that "0" means the upper-left corner and "1" means the lower-right corner?

The Offset property relies on two other properties, which are defined within the LinearGradientBrush definition itself. These two System.Windows.Point -based properties are called StartPoint and EndPoint and ultimately determine the beginning and ending of a gradient. Collectively, these two properties define a rectangular boundary that the Offset property works within. This coordinate space can best be visualized as the following (figure 8.1), where each corner displays a Point value.

Figure 8.1. The Brush coordinate space

By default, the StartPoint property is set to represent the upper-left corner (0, 0) of this coordinate space. Conversely, the EndPoint defaults to represent the lower-right corner (1, 1) of the coordinate space. You can manipulate both property values to take full control of the range within which the gradient occurs, as well as the direction. Imagine taking the previous snippet and making the gradient run horizontally instead of diagonally. This can be accomplished by altering the StartPoint and EndPoint property values as shown in snippet 8.17.

Snippet 8.17. XAML Result: A basic LinearGradientBrush rendered horizontally

<Ellipse Stroke="Black" StrokeThickness="3"
Width="64" Height="64" Canvas.Left="8" Canvas.Top="8">
<Ellipse.Fill>
<LinearGradientBrush StartPoint="0,0" EndPoint="1,0">
<GradientStop Color="Navy" Offset="0" />
<GradientStop Color="White" Offset="1" />
</LinearGradientBrush>
</Ellipse.Fill>
</Ellipse>

Although you could have rotated the imaginary gradient line by altering the Offset property values of each of the GradientStop elements, the StartPoint and EndPoint properties give you control over the entire range of the gradient. This fact becomes particularly important when you begin to consider using multiple color transitions.

Both the LinearGradientBrush and the RadialGradientBrush, which you’ll see shortly, allow you to define as many GradientStop elements as you want. As more and more GradientStop elements are added, the more important it is to understand the relationship between the Offset property within the StartPoint and EndPoint properties. Snippet 8.18 shows how to use multiple GradientStop elements by adjusting the Offset property.

Snippet 8.18. XAML Result: A horizontal LinearGradientBrush with multiple transitions

<Rectangle StrokeThickness="0" Width="200"
Height="64" Canvas.Left="8" Canvas.Top="8">
<Rectangle.Fill>
<LinearGradientBrush StartPoint="0,0" EndPoint="1,0">
<GradientStop Color="Yellow" Offset="0" />
<GradientStop Color="Orange" Offset=".45" />
<GradientStop Color="Blue" Offset=".55" />
<GradientStop Color="Green" Offset="1" />
</LinearGradientBrush>
</Rectangle.Fill>
</Rectangle>

As the previous snippets have shown, the LinearGradientBrush provides you with a lot of opportunity to add richness to your applications. Occasionally, you may want to provide a sense of depth to your graphics. Although Silverlight supports only 2D graphics, you can still deliver the illusion of depth by using a RadialGradientBrush.

8.3.3. RadialGradientBrush

The RadialGradientBrush is very similar to the LinearGradientBrush except that the color transitions begin from an originating Point. As the Brush radiates from the center, it gradually paints elliptical transitions until a GradientStop is encountered. This process continues from one GradientStop to the next until each one has been rendered. Snippet 8.19 illuminates a basic RadialGradientBrush.

Snippet 8.19. XAML Result: A basic RadialGradientBrush

<Ellipse Width="75" Height="75" Stroke="Black">
<Ellipse.Fill>
<RadialGradientBrush>
<GradientStop Color="Black" Offset="0"/>
<GradientStop Color="Black" Offset="1"/>
<GradientStop Color="Gray" Offset="0.5"/>
</RadialGradientBrush>
</Ellipse.Fill>
</Ellipse>

As this snippet shows, the brush begins at the center of the Ellipse by default. This originating Point can be customized in one of two ways. The first approach involves specifying a Point value within the Center property. The Center Point represents the focal point of the outermost ellipse of the gradient. Alternatively, or in conjunction with the Center, you can use the GradientOrigin property to specify the Point that defines where the radial gradient emanates from.

As a radial gradient is rendered, it grows from the GradientOrigin in a circular fashion. Sometimes it’s necessary to use a more elliptical gradient instead of a pure circular effect. To define an elliptical gradient, you need to utilize the RadiusX and RadiusY properties, which are consistent with the properties of the same name from the Ellipse element. Snippet 8.20 compares several ellipses using different RadiusX and RadiusY properties, which both default to ".5".

Snippet 8.20. XAML Result: Comparing various uses of the RadiusX and RadiusY properties of the RadialGradientBrush

<Canvas Width="245" Height="75" Background="White">
<Ellipse Width="75" Height="75" Stroke="Black">
<Ellipse.Fill>
<RadialGradientBrush>
<GradientStop Color="Black" Offset="0"/>
<GradientStop Color="Black" Offset="1"/>
<GradientStop Color="Gray" Offset="0.5"/>
</RadialGradientBrush>
</Ellipse.Fill>
</Ellipse>
<Ellipse Width="75" Height="75" Canvas.Left="85"
Stroke="Black">
<Ellipse.Fill>
<RadialGradientBrush RadiusX=".25">
<GradientStop Color="Black" Offset="0"/>
<GradientStop Color="Black" Offset="1"/>
<GradientStop Color="Gray" Offset="0.5"/>
</RadialGradientBrush>
</Ellipse.Fill>
</Ellipse>
<Ellipse Width="75" Height="75" Canvas.Left="170"
Stroke="Black">
<Ellipse.Fill>
<RadialGradientBrush RadiusY=".25">
<GradientStop Color="Black" Offset="0"/>
<GradientStop Color="Black" Offset="1"/>
<GradientStop Color="Gray" Offset="0.5"/>
</RadialGradientBrush>
</Ellipse.Fill>
</Ellipse>
</Canvas>

As the previous snippets show, you can use a GradientBrush to provide basic linear and radial effects. Although these Brush elements are appropriate in certain situations, occasionally you need to deliver a richer, more textured effect. Textures are often delivered via images, which can be painted on visual elements using an ImageBrush.

8.3.4. ImageBrush

The ImageBrush allows you to fill an area with an image instead of a solid or shifting color. The ImageBrush utilizes a picture specified within the ImageSource property to paint a raster graphic. This Brush supports both .jpg and.png formats to deliver a textured effect to your visual elements. Snippet 8.21 shows a basic ImageBrush using our old friend from chapter 7, Walker.

Snippet 8.21. XAML Result: An example of an ImageBrush

<Ellipse Width="60" Height="60" Stroke="Black">
<Ellipse.Fill>
<ImageBrush ImageSource="http://www.silverlightinaction.com/man.png" />
</Ellipse.Fill>
</Ellipse>

As you can imagine, an ImageBrush can easily add a rich, vibrant touch to your painting surface. Sometimes, you may want your painting surface to be more dynamic and livelier. With the same type of simplicity as the ImageBrush, you can paint a surface with a video, using the VideoBrush.

8.3.5. VideoBrush

Imagine watching a shooting star speed across the night sky through the elliptical eyepiece of a telescope. With the VideoBrush in action, you can deliver this type of scene by drawing an Ellipse and filling it with a MediaElement. Snippet 8.22 shows exactly how to use the amazing VideoBrush.

Snippet 8.22. XAML: An example of a VideoBrush
<MediaElement x:Name="myMediaElement" Opacity="0"
Source="http://www.silverlightinaction.com/video2.wmv" />
<Ellipse Width="100" Height="100" Stroke="Black">
<Ellipse.Fill>
<VideoBrush SourceName="myMediaElement" />
</Ellipse.Fill>
</Ellipse>

As this snippet shows, the VideoBrush references a MediaElement through the SourceName property. This fact allows you to manipulate the playback functionality of a VideoBrush by altering the playback of the MediaElement as defined within chapter 7. If you want to pause or stop the video displayed within a VideoBrush, you call the Pause() or Stop() methods of the MediaElement that the VideoBrush references.

Up to this point, the Brush elements have been used in relation to a basic Ellipse. An Ellipse was chosen for the sake of illustration; you can use all the Brush elements that we’ve covered within any number of visual elements including, but not limited to, a Canvas, a TextBox, or even a TextBlock, as snippet 8.23 shows.

Snippet 8.23. XAML: An example of a VideoBrush within a TextBlock
<MediaElement x:Name="myMediaElement" Opacity="0"
Source="http://www.silverlightinaction.com/video2.wmv" />
<TextBlock Text="HELLO" FontFamily="Verdana"
FontSize="80" FontWeight="Bold">
<TextBlock.Foreground>
<VideoBrush SourceName="myMediaElement" />
</TextBlock.Foreground>
</TextBlock>

This sample only begins to show the potential allotted by the different Brush elements. All the Brush options are usable within any property that has a Brush type. In addition to these rich Brush options, Silverlight supports a lively set of features that allows you to get a new perspective on your visual elements. These features are known as transforms.

8.4. Transforms

The Transform element gives you the flexibility to alter the appearance of any UIElement within Silverlight. Transforms give you the flexibility to change the size, location, gyration, and angling apart from the other related properties that have been defined up to this point. The real value of transforms will become apparent when you learn about animations in the next chapter. But first, let’s look at the ways UIElement objects can be altered.

As table 8.3 describes, each Transform has its own special purpose. As you’ll see within each of these sections, applying a transformation generally involves altering one or two basic properties.

Table 8.3. A list of the available transformation options

Transform

Description

RotateTransform Revolves an object by a specific Angle.
ScaleTransform Provides a zoom in or out effect by specified amounts.
SkewTransform Tilts an element by defined amounts.
TranslateTransform Moves an element by specified amounts.

8.4.1. RotateTransform

The RotateTransform is responsible for rotating an object clockwise around a specified point by a specified angle. This rotation affects the local coordinate system of the rotated object. If you need to rotate an object in place, you need to specify the center point as the center of the object being rotated. Snippet 8.24 shows a basic square rotated clockwise by 30 degrees. The dashed version represents the original square before the transform was applied.

Snippet 8.24. XAML Result: A square that has been rotated by 30 degrees

<Rectangle Width="50" Height="50" Fill="Green" Stroke="Black">
<Rectangle.RenderTransform>
<TransformGroup>
<RotateTransform Angle="30"/>
</TransformGroup>
</Rectangle.RenderTransform>
</Rectangle>

The Angle property specifies to rotate clockwise around the optional CenterX and CenterY properties, which default to 0. Because these values are initially set to 0, an element will rotate around the upper-left corner. If you set these values to the center of the object you’re rotating, it will give the element the appearance of rotating in place.

When rotating elements, sometimes it becomes necessary to rotate them counterclockwise. As you may have already guessed, you perform this task by providing a negative value within the Angle property. Note that an element will complete one full rotation if the Angle is set to 360 or -360.

8.4.2. ScaleTransform

The ScaleTransform enables you to expand or contract an object horizontally or vertically, empowering you to create the effect of zooming in or out. Snippet 8.25 shows how a basic square was zoomed in on via a ScaleTransform.

Snippet 8.25. XAML Result: A square that has been scaled by a magnitude of 2.5

<Rectangle Width="30" Height="30" Fill="Green"
Stroke="Black" Canvas.Left="35" Canvas.Top="35">
<Rectangle.RenderTransform>
<TransformGroup>
<ScaleTransform ScaleX="2.5" ScaleY="2.5"/>
</TransformGroup>
</Rectangle.RenderTransform>
</Rectangle>

The ScaleX and ScaleY properties determine the magnitude to zoom in/out by. As you may expect, the ScaleX property stretches or shrinks the element along the x-axis. The ScaleY property stretches or shrinks the element along the y-axis. If you provide the same value in both properties, the object will expand or contract proportionally.

You may have also noticed that the Rectangle expands from the upper-left corner. This is because the CenterX and CenterY properties determine the point from where the scale operation should take place. By default, these values are set to 0.

8.4.3. SkewTransform

A SkewTransform warps the coordinate space in a divergent manner. By skewing, or shearing, an element, you basically slant the element in a direction. Snippet 8.26 illustrates a basic square skewed by 18 degrees on both the x and y-axes.

Snippet 8.26. XAML Result: A Rectangle that has been skewed by 18 degrees

<Rectangle Width="75" Height="75" Fill="Green"
Stroke="Black" Canvas.Left="12" Canvas.Top="12">
<Rectangle.RenderTransform>
<TransformGroup>
<SkewTransform AngleX="18" AngleY="18"/>
</TransformGroup>
</Rectangle.RenderTransform>
</Rectangle>

The AngleX and AngleY properties specify the amount to shear the rectangle horizontally and vertically. Much like the other transforms we’ve reviewed, the SkewTransform also exposes a CenterX and CenterY property to specify the horizontal and vertical origin of the skew rendering.

8.4.4. TranslateTransform

The TranslateTransform element allows you to define how to transfer an element from one location to another. Snippet 8.27 shows a square translated by 25 pixels vertically and horizontally.

Snippet 8.27. XAML Result: A basic translation in action

<Rectangle Width="50" Height="50" Fill="Green" Stroke="Black">
<Rectangle.RenderTransform>
<TransformGroup>
<TranslateTransform X="25" Y="25"/>
</TransformGroup>
</Rectangle.RenderTransform>
</Rectangle>

As this snippet demonstrates, by specifying a double-precision floating-point value within the X and Y properties of a TranslateTransform, you can move a visual element horizontally or vertically. As you can imagine, the TranslateTransform, as well as the other transforms mentioned, give you a lot of flexibility with your visual elements. These transforms can be used to provide even more radical changes when you group them.

8.4.5. TransformGroup

In the previous transform-related examples, you may have noticed the Transform-Group element. This element empowers you to simultaneously define multiple transformations on a visual element.

Up to this point, we’ve primarily used a Rectangle as the visual element for transformations, but you can also apply these interesting renderings to any UIElement. You can apply these transformations to items such as TextBox elements, Buttons, the MediaElement, and so many more that you’ll need to refer to the Silverlight SDK to see. For the sake of illustration, all the transforms that have been discussed are applied to the following TextBox (snippet 8.28).

Snippet 8.28. XAML Result: A sample of all of the transforms on a TextBox. Notice how the content of the TextBox is also transformed as the text is entered

<TextBox x:Name="myTextBox" Height="30" Width="90">
<TextBox.RenderTransform>
<TransformGroup>
<RotateTransform Angle="15" />
<SkewTransform AngleX="10" AngleY="10" />
<ScaleTransform ScaleX="2" ScaleY="2" />
<TranslateTransform X="10" Y="10" />
</TransformGroup>
</TextBox.RenderTransform>
</TextBox>

Although this snippet is a bit excessive, it does accurately display the true flexibility allotted by the transform elements. Collectively, these elements put the icing on the graphical cake.

8.5. Blending it together

Throughout this chapter, you’ve learned about Silverlight’s rich graphical capabilities. In this section, you’ll see how to create some of these graphical features using Blend. From there, you’ll learn how to write the code that responds to a user’s actions to interact with a graphical element. When all is said and done, you’ll have an application that looks like that in figure 8.2.

Figure 8.2. The end result of this Blending it Together section.

Although this item doesn’t look like much, you’ll be able to interact with the ball in the application. By pressing the A or D key on the keyboard, you’ll be able to rotate the ball left or right. Alternatively, if you press W or S, the ball will expand or contract. To begin this exercise, you first create the ellipse that will eventually represent the ball.

Walk-through 8.1. Creating an ellipse within Expression Blend 2

1.  

Launch Microsoft Expression Blend.

2.  

Select File > New Project…

3.  

From the New Project Dialog, select a Silverlight 2 Application and enter the following properties:

4.  

From the toolbox, along the left side of the screen, expand the Shape Tools option list and select the Ellipse tool.

[Unsupported tag: inlinemediaobject/caption]

Click and hold down the “Rectangle” tool to expand the “Shape tools” option list.

[Unsupported tag: inlinemediaobject/caption]

Select the “Ellipse” tool.

5.  

With the Ellipse tool selected, draw an ellipse on the LayoutRoot within the Page.xaml tab.

[Unsupported tag: inlinemediaobject/caption]

From the upper-left corner of the LayoutRoot, hold down the left mouse button and drag down and to the right.

[Unsupported tag: inlinemediaobject/caption]

Continue to drag the mouse until the ellipse has a height and width of approximately 100. Then release the mouse button.

[Unsupported tag: inlinemediaobject/caption]

When done, your ellipse should look similar to this.

6.  

Open the Properties panel by selecting the Properties tab in the upper-right corner of Blend.

[Unsupported tag: inlinemediaobject/caption]

The Properties tab is located within the upper-right corner of Blend.

When done, your ellipse should look similar to this.

7.  

Within the Name field, enter myEllipse.

8.  

Toward the bottom of the Properties panel is the Layout category. Scroll down to it and verify that the Width property is set to 100. Also verify that the Height property is set to 100. Then set the Left and Top margin properties to 150.

9.  

Along the left one-third of Blend, find the Objects and Timeline category within the Interaction panel. Then select the UserControl item.

[Unsupported tag: inlinemediaobject/caption]

Locate the Objects and Timeline category within Blend.

Click the Page item to select the root Canvas.

10.  

With the UserControl element selected, go to the Properties panel (along the right side of Blend again). Set the Width and Height properties to 400 within the Layout category.

11.  

Press F5 to run the application.

12.  

Leave Microsoft Expression Blend open!

Now that the ellipse has been created, you need to add some dimension to make it look like a ball. To accomplish this, you utilize the RadialGradientBrush you learned about earlier, but now you create a RadialGradientBrush within Blend.

Walk-through 8.2. Turning an ellipse into a ball within Expression Blend 2

1.  

With the project created within walk-through 8.1 open, select the myEllipse element from the Objects and Timeline. If you need to, refer to walk-through 8.1, step 9 for related images.

2.  

With myEllipse selected, locate the Brushes category within the Properties panel.

[Unsupported tag: inlinemediaobject/caption]

The Brushes category is located toward the top of the Properties panel.

3.  

Select the Fill brush.

4.  

Select the Gradient Brush tab.

[Unsupported tag: inlinemediaobject/caption]

By default, the Solid Color Brush is selected.

[Unsupported tag: inlinemediaobject/caption]

Click the Gradient Brush tab.

5.  

Select the Radial Gradient within the lower-left corner of the Brushes category.

[Unsupported tag: inlinemediaobject/caption]

Select the Radial Gradient brush within the lower-left corner of the Brushes category.

6.  

Change the leftmost gradient stop within the gradient slider to White. (It will automatically change to #FFFFFF.) Then, select the rightmost gradient stop and change the value to Navy. (The color value will automatically change to #FF000080.)

[Unsupported tag: inlinemediaobject/caption]

Click the leftmost gradient stop, and then change the value to White.

[Unsupported tag: inlinemediaobject/caption]

Click the rightmost gradient stop, and then change the value to Navy.

7.  

At this point, your ellipse should look like the following:

8.  

Now let’s transform the fill to make the ball look like there’s a light shining on it. First, select the Brush Transform tool from the toolbox.

[Unsupported tag: inlinemediaobject/caption]

The Brush Transform tool is located within the toolbox, which is located on the far left side of Blend.

9.  

With the Brush Transform tool selected, click the opening end of the transform arrow and drag to the lower-left corner of the ellipse. Then, rotate the transformation to the 10 o’clock position within the ellipse.

[Unsupported tag: inlinemediaobject/caption]

Drag the open end of the transform arrow to the lower-right corner or the ellipse.

[Unsupported tag: inlinemediaobject/caption]

Once in the lower-left corner, rotate the transformation to the 10 o’clock position.

[Unsupported tag: inlinemediaobject/caption]

Once completed, your ellipse will look like this.

10.  

Press F5 to run the application.

At this point, you have something that looks much richer and vibrant. Let’s now turn our focus to the code-behind file related to this XAML to add interactivity. As mentioned earlier, the goal is to rotate the ball if the user selects either the A or D key. In addition, if the user selects either the W or S key, the ball will expand or contract. You’ll accomplish this functionality by taking advantage of the KeyEventArgs you learned about in chapter 3, in conjunction with the RotateTransform and Scale-Transform you learned about in this chapter.

Walk-through 8.3. Adding event handlers to the ball in Visual Studio

1.  

Launch Microsoft Visual Studio 2008.

2.  

Select File > Open > Project/Solution…, and select the project started within walk-through 8.1.

3.  

Open the Page.xaml.cs file from the Solution Explorer.

4.  

Add the following privately visible fields, which represent the scale and rotate transformations within the class definition:

// The transformation to increase/decrease the ball by
private ScaleTransform scaleTransform = new ScaleTransform();

// The transformation to rotate the ball by
private RotateTransform rotateTransform = new RotateTransform();

5.  

Add the following method to the class to define the initial amount the ellipse is scaled by and where the ellipse will grow and shrink from:

private void InitializeScaleTransform()
{
// Set the inital scale at nothing
scaleTransform.ScaleX = 1.0;
scaleTransform.ScaleY = 1.0;

// Put the focal point at the center of the ball
scaleTransform.CenterX = 50.0;
scaleTransform.CenterY = 50.0;
}

6.  

Add the following method to the class to define the initial rotation and specify that you want to rotate around the center of the ellipse:

private void InitializeRotateTransform()
{
rotateTransform.Angle = 0.0;
rotateTransform.CenterX = 50.0;
rotateTransform.CenterY = 50.0;
}

7.  

Add the following method to the class to connect the transformations to the ellipse:

private void InitializeTransformGroup()
{
TransformGroup transformGroup = new TransformGroup();
transformGroup.Children.Add(rotateTransform);
transformGroup.Children.Add(scaleTransform);
myEllipse.RenderTransform = transformGroup;
}

8.  

Now, within the constructor of the class, place calls to the three previously defined methods. When finished, your constructor will look like the following:

public Page()
{
// Required to initialize variables
InitializeComponent();

// Initialize the transformations
InitializeScaleTransform();
InitializeRotateTransform();
InitializeTransformGroup();
}

9.  

Open the Page.xaml file.

10.  

Within the UserControl element, add an event handler for the KeyDown event by adding the following attribute to the UserControl:

KeyDown="UserControl_KeyDown"

11.  

Go back to the Page.xaml.cs file and ensure that there’s a using directive for the System.Windows.Input namespace. If it isn’t there, add it along with the other using directives.

12.  

Within the Page.xaml.cs file, create the definition for the event handler. Add the following code within the definition of the class: private void UserControl_KeyDown(object sender, KeyEventArgs e)

private void UserControl_KeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.A) // A-Key (Left)
rotateTransform.Angle -= 3.0;
else if (e.Key == Key.D) // D-Key (Right)
rotateTransform.Angle += 3.0;
else if (e.Key == Key.S) // S-Key (Down)
{
scaleTransform.ScaleX *= .97;
scaleTransform.ScaleY *= .97;
}
else if (e.Key == Key.W) // W-Key (Up)
{
scaleTransform.ScaleY *= 1.03;
scaleTransform.ScaleX *= 1.03;
}
else if (e.Key == Key.R) // R-Key (Reset)
{
InitializeScaleTransform();
InitializeRotateTransform();
}
}

13.  

Press F5 to run the application.

At this point, you should have a ball that resembles that in figure 8.2. Although fairly trivial, it still combines a number of the elements you learned about throughout this chapter.

8.6. Summary

Silverlight’s inherent graphical capabilities go far beyond cartoons and visual fireworks. By shaping these elements into illustrations, graphics can provide a bridge to your users to help them connect with difficult concepts. These valuable illustrations can be composed of a series of shapes compiled from arcs, curves, and lines. These shapes can then be filled with radiating colors or textured visuals such as images and videos.

Although the rich graphical capabilities are incredibly valuable and immersive, there is one way you can make these graphics even more engaging. With the help of the transforms and a timeline, you can create engaging animations that are sure to connect with your users. Please turn to the next chapter to learn how to incorporate animations into your Silverlight applications.