- Graphics Principles
- Graphics Elements
- Under the Hood
- Where Are We?
Graphics Elements
As previously discussed, the Silverlight runtime can load and draw vector graphics XAML on a variety of platforms. The graphics elements can also be specified programmatically when you use C# or JavaScript. This section describes the elements you can use to display vector graphics and images in your applications.
Shapes
This section starts the graphics element discussion with the base class for all graphics primitives: the Shape element. The Shape element provides the following:
- A Fill property to specify how the shape interior is drawn
- A Stretch property to indicate how a Shape element stretches to a specified Width and Height
- Stroke and StrokeThickness properties to specify the pen behavior
The elements that derive from Shape define the shape's geometry. These elements include Rectangle, Ellipse, Line, Polygon, Polyline, and Path.
Rectangle
To draw a rectangle at a specified position, place it in a Canvas element and specify its Canvas.Top, Canvas.Left, Width, Height, and Fill color:
<Canvas xmlns="http://schemas.microsoft.com/client/2007"> <Rectangle Fill="LightGray" Canvas.Left="50" Canvas.Top="50" Width="400" Height="400" /> </Canvas>
You can add an outline to the rectangle as shown in Figure 3.6 by setting the Stroke property to specify the color and the StrokeThickness property to specify the thickness:
<Canvas xmlns="http://schemas.microsoft.com/client/2007"> <Rectangle Fill="LightGray" Stroke="Black" StrokeThickness="10" Canvas.Left="50" Canvas.Top="50" Width="400" Height="400" /> </Canvas>
Figure 3.6 Rectangle element with an outline
You can use the Rectangle element to draw the rounded rectangles shown in Figure 3.7 by specifying the RadiusX and RadiusY properties:
<Canvas xmlns="http://schemas.microsoft.com/client/2007"> <Rectangle Fill="LightGray" Stroke="Black" StrokeThickness="10" RadiusX="40" RadiusY="60" Canvas.Left="50" Canvas.Top="50" Width="400" Height="400" /> </Canvas>
Figure 3.7 Rectangle element with rounded corners
Ellipse
As with the Rectangle element, you can position an Ellipse element with the same Canvas.Top, Canvas.Left, Width, and Height properties. Silverlight stretches the ellipse to fit the specified bounds as shown in Figure 3.8.
Figure 3.8 Ellipse element
<Canvas xmlns="http://schemas.microsoft.com/client/2007"> <Ellipse Fill="LightGray" Stroke="Black" StrokeThickness="10" Canvas.Left="50" Canvas.Top="50" Width="400" Height="400" /> </Canvas>
Line
To draw a line, you can use the Line element and set its X1, Y1, X2, Y2 properties. As with all other shapes, you can use the Stroke property to specify the fill color and the StrokeThickness property to specify the thickness as shown in Figure 3.9.
Figure 3.9 Line element
<Canvas xmlns="http://schemas.microsoft.com/client/2007"> <!–– thick diagonal line ––> <Line Stroke="Black" StrokeThickness="40" X1="60" Y1="60" X2="400" Y2="400" /> <!–– one pixel horizontal line ––> <Line Stroke="Black" StrokeThickness="1" X1="100" Y1="60" X2="400" Y2="60" /> </Canvas>
If you look closely at the pixels for the horizontal line shown in Figure 3.10, you will see it has a height of two pixels despite the one pixel StrokeThickness specified in the XAML. Furthermore, the line is gray instead of the specified black color. To understand this rendered result, consider the following equivalent Rectangle element:
<Canvas xmlns="http://schemas.microsoft.com/client/2007"> <!–– one pixel horizontal line drawn as a rectangle ––> <Rectangle Fill="Black" Canvas.Left="99.5" Canvas.Top="59.5" Width="301" Height="1" /> </Canvas>
Figure 3.10 Pixels rendered for a Line element
The previous Rectangle element has half integer values for its position. The reason for the half pixel coordinates is that the line expands by half StrokeThickness in either direction. Because StrokeThickness is equal to one, the line adjusts the top and left coordinates by -0.5. Because the rectangle is between two pixels, it gets anti-aliased and occupies two pixels with a lighter color. If you want sharp horizontal and vertical lines, you should draw a rectangle positioned at integer coordinates to get the result shown in Figure 3.11.
Figure 3.11 Sharp horizontal line drawn with a Rectangle element
<Canvas xmlns="http://schemas.microsoft.com/client/2007"> <!–– one pixel horizontal line drawn as a rectangle ––> <Rectangle Fill="Black" Canvas.Left="99" Canvas.Top="59" Width="301" Height="1" /> </Canvas>
Path
The Path element extends the Shape element by providing a Data property that specifies the geometry object. The Rectangle, Ellipse, and Line elements previously discussed are all expressible with the more general Path element. For example, instead of specifying a Rectangle element, we can specify a Path element with a RectangleGeometry:
<Canvas xmlns="http://schemas.microsoft.com/client/2007"> <Path Fill="Blue" Stroke="Black" StrokeThickness="10" > <Path.Data> <RectangleGeometry Rect="96,160,256,224"/> </Path.Data> </Path> </Canvas>
The Path element syntax is more verbose than the specialized shapes syntax. However, because Silverlight converts all shapes internally to Path elements, if you understand how the Path element draws you will understand how all shapes draw.
In addition to expressing specialized shapes, the Path element can express a geometry consisting of a collection of Figure elements. A Figure element is a connected set of line segments and Bezier segments. The most common method to specify these figures, line segments, and curves is the path mini-language. For example, to draw the shape in Figure 3.12 you would do the following:
Figure 3.12 Example path
<Canvas xmlns="http://schemas.microsoft.com/client/2007"> <Path StrokeThickness="10" Stroke="Black" Fill="Red" Data="M 14,16 C 14,16 -8,256 64,352 C 136,448 185,440 247,336 C 309,233 448,416 448,416 L 436,224 Z" /> </Canvas>
The commands supported by the mini-language include those shown in Table 3.1. Each command is followed by the action it takes.
Table 3.1. Path Mini-language Commands
Command |
Action |
M x,y |
Move to position x,y |
L x,y |
Draw a line from the current position to position x,y |
C x1,y1, x2,y2, x3,y3 |
Draw a cubic Bezier segment with control points consisting of the current position, (x1,y1), (x2,y2), and (x3,y3) |
Q x1,y1, x2,y2 |
Draw a quadratic Bezier segment with control points consisting of the current position, (x1,y1), and (x2,y2) |
H x |
Draw a horizontal line from the current position x0,y0 to position x,y0 |
V y |
Draw a vertical line from the current position x0,y0 to position x0,y |
Z |
Close a figure |
F0 |
Specify EvenOdd fill rule |
F1 |
Specify NonZero fill rule |
An alternative form of specifying the geometry is to use the expanded XAML syntax:
<Canvas xmlns="http://schemas.microsoft.com/client/2007"> <Path StrokeThickness="10" Stroke="Black" Fill="Red" > <Path.Data> <PathGeometry> <PathGeometry.Figures> <PathFigure StartPoint="14, 16" IsClosed="true"> <PathFigure.Segments> <BezierSegment Point1="14,16" Point2="-8,256" Point3="64,352" /> <BezierSegment Point1="136,448" Point2="185,440" Point3="247,336" /> <BezierSegment Point1="309,233" Point2="448,416" Point3="448,416" /> <LineSegment Point="436,224" /> </PathFigure.Segments> </PathFigure> </PathGeometry.Figures> </PathGeometry> </Path.Data> </Path> </Canvas>
One additional concept previously discussed is that of a Figure element. Because the Path element can have more than one figure, it can create an empty space in the middle of a filled space as shown in Figure 3.13.
Figure 3.13 Path with an empty space in the center
<Canvas xmlns="http://schemas.microsoft.com/client/2007"> <Path StrokeThickness="10" Stroke="Black" Fill="LightGray" Data="M 50,50 L 50,450 450,450 450,50 50,50 z M 100,100 L 100,400 400,400 400,100 100,100 z" /> </Canvas>
Fill Rules
The previous section explained how to use geometry to specify an outline of an area to fill. However, an outline does not uniquely specify the inside and outside of the shape. For example, the outline in Figure 3.13 could generate any of the rendering results shown in Figure 3.14. A fill rule is a method to distinguish the inside of a shape from the outside of the shape.
Figure 3.14 Different fills for the same outline
One approach to specifying what is inside a shape is to cast a horizontal line through the shape and count the number of edges crossed from left to right. If the number of edges crossed is even, classify the horizontal line segment as outside the shape. If the number of edges is odd, classify that segment as inside the shape. This fill rule is the EvenOdd rule and is the default fill mode for Path elements. To specify the fill mode explicitly, you can specify the FillRule property on geometry or use F0 for EvenOdd from the path mini-language:
<Canvas xmlns="http://schemas.microsoft.com/client/2007"> <!–– Path with fill rule F0 = EvenOdd ––> <Path StrokeThickness="10" Stroke="Black" Fill="LightGray" Data="F0 M 50,50 L 50,450 450,450 450,50 50,50 z M 100,100 L 100,400 400,400 400,100 100,100 z" /> </Canvas>
An alternative rule is the NonZero rule, which considers the order points are specified in the input. If an edge goes up in the y direction, assign that edge a + 1 winding number. If an edge goes down in the y direction, assign that edge a – 1 winding number. The NonZero rule defines the interior of the shape to be all those segments where the sum of winding numbers from the left to the current segment is not zero. For example, if you specify the geometry shown in Figure 3.14 with the point order in the following markup, it would result in the winding numbers and filled segments shown in Figure 3.15.
Figure 3.15 Winding mode numbers resulting in a filled center space
<Canvas xmlns="http://schemas.microsoft.com/client/2007"> <!–– Path with fill rule F1 = NonZero ––> <Path StrokeThickness="10" Stroke="Black" Fill="LightGray" Data="F1 M 50,50 L 50,450 450,450 450,50 50,50 z M 100,100 L 100,400 400,400 400,100 100,100 z" /> </Canvas>
If you specify the points in the following order, the shape would render differently as shown in Figure 3.16.
Figure 3.16 Different fill as a result of a different point order
<Canvas xmlns="http://schemas.microsoft.com/client/2007"> <!–– Path with fill rule F1 = NonZero ––> <Path StrokeThickness="10" Stroke="Black" Fill="LightGray" Data="F1 M 50,50 L 50,450 450,450 450,50 50,50 z M 100,100 L 400,100 400,400 100,400 100,100 z" /> </Canvas>
Images
In addition to the vector graphics elements previously discussed, the other fundamental graphics primitive is the Image element. To display an image, you can use the Image element with a reference to a URI to produce the result shown in Figure 3.17.
Figure 3.17 Image element example
<Canvas xmlns="http://schemas.microsoft.com/client/2007"> <Image Source="silverlight.png"/> </Canvas>
The Source property can reference any image in JPG or PNG format. However, if the JPG or PNG contains DPI (dots per inch) information, Silverlight ignores this information because it is usually not accurate for display purposes. All references to URIs in XAML are relative to the location of the XAML file. For example, if the XAML file is in a XAP, Silverlight searches for silverlight.png in the XAP. If the XAML file is a resourced in a managed assembly, Silverlight searches for silverlight.png in that same assembly.
If you do not specify the Width and Height properties of an image, Silverlight draws the image at its natural size, which results in a pixel perfect rendering of the original image data.
Brushes
All of the previous examples filled the Path element pixels with a single color. Silverlight also supports filling arbitrary shapes with image brushes, linear gradient brushes, and radial gradient brushes. A brush is a mathematical function that maps an (x,y) position to a color. For example, SolidColorBrush is a simple function that ignores its position and always outputs the same color. This section describes the brushes available in Silverlight and includes the function used to map from screen position to color.
Solid Color Brush
SolidColorBrush returns a single color for all screen positions. When you specify a Fill or Stroke property value, Silverlight creates a solid color brush for the corresponding shape fill or pen stroke respectively:
<Canvas xmlns="http://schemas.microsoft.com/client/2007"> <Rectangle Fill="Blue" Stroke="Black" StrokeThickness="10" Canvas.Left="96" Canvas.Top="160" Width="256" Height="224" /> </Canvas>
Alternatively, you can specify a brush with the expanded XAML syntax:
<Canvas xmlns="http://schemas.microsoft.com/client/2007"> <Rectangle StrokeThickness="10" Canvas.Left="96" Canvas.Top="160" Width="256" Height="224" > <Rectangle.Fill> <SolidColorBrush Color="Blue"/> </Rectangle.Fill> <Rectangle.Stroke> <SolidColorBrush Color="Black"/> </Rectangle.Stroke> </Rectangle> </Canvas>
The previous examples specified a named color. You can also specify a color explicitly by providing a hex string of the form #aarrggbb, which represents a hex alpha channel value, red channel value, green channel value, and blue channel value. For example, opaque green would be #ff00ff00.
From C#, you can specify a color by creating an instance of the Color class:
Color green = Color.FromArgb(0xff, 0x0, 0xff, 0x0);
The alpha channel specifies the degree of transparency where 0x0 indicates completely transparent, 0xff indicates an opaque color, and intermediate values indicate partial transparency. A brush with a transparent color will blend its color to the background color using the following formula:
Color_destination = (alpha * Color_source + (0xff – alpha) * Color_destination)/256
Silverlight will pass colors specified in hex format to the Web browser without a color space conversion. Typically, the browser will interpret the color as a standard RGB color space color (sRGB) that is, an 8-bit per channel 2.2 implied gamma space color. However, the visible color may vary with operating systems, Web browser, the monitor, and the operating system color profile. An alternative form of specifying colors is the floating point wide gamut linear RGB color space (scRGB):
<Canvas xmlns="http://schemas.microsoft.com/client/2007"> <Rectangle Fill="sc#1, 1.0, 0.0, 0.0" Canvas.Left="96" Canvas.Top="160" Width="256" Height="224" /> </Canvas>
Silverlight converts scRGB colors to sRGB internally for blending. Consequently, specifying colors in native sRGB is desirable to avoid extra color conversion steps.
Gradient Brushes
Gradient brushes define a set of colors and positions along a virtual gradient line. The function that maps screen position to color first maps the screen position to a point along the gradient line and then interpolates a color based on the two nearest points.
Consider the following use of a LinearGradientBrush:
<Canvas xmlns="http://schemas.microsoft.com/client/2007"> <Ellipse Width="450" Height="450" > <Ellipse.Fill> <LinearGradientBrush StartPoint="0,0" EndPoint="1,1"> <LinearGradientBrush.GradientStops> <GradientStop Color="White" Offset="0"/> <GradientStop Color="Black" Offset="1"/> </LinearGradientBrush.GradientStops> </LinearGradientBrush> </Ellipse.Fill> </Ellipse> </Canvas>
The preceding linear gradient brush produces the fill shown in Figure 3.18. A linear gradient brush maps a screen position to the point on the line closest to that position. The brush then interpolates a color based on the two nearest points specified along the line as shown in Figure 3.18.
Figure 3.18 Linear gradient brush
Alternatively, you can specify a radial gradient fill using RadialGradientBrush that takes the distance from the screen position to the center of the radial gradient and maps that distance to the specified gradient line of colors and positions. For example, the following XAML generates the rendering shown in Figure 3.19.
Figure 3.19 Radial gradient brush
<Canvas xmlns="http://schemas.microsoft.com/client/2007"> <Ellipse Width="450" Height="450" > <Ellipse.Fill> <RadialGradientBrush> <RadialGradientBrush.GradientStops> <GradientStop Color="White" Offset="0"/> <GradientStop Color="Black" Offset="1"/> </RadialGradientBrush.GradientStops> </RadialGradientBrush> </Ellipse.Fill> </Ellipse> </Canvas>
Another feature of RadialGradientBrush is the capability to move the point that maps to the start of our gradient line. In particular, in our previous example, the center of the radial gradient circle mapped to the start of our gradient line and the radius of the gradient circle mapped to the end of our gradient line. With this pattern, you always get a uniform ellipse. You can move the center using the GradientOrigin property to get the result shown in Figure 3.20.
Figure 3.20 Focal gradient brush
<Canvas xmlns="http://schemas.microsoft.com/client/2007"> <Ellipse Width="450" Height="450" > <Ellipse.Fill> <RadialGradientBrush GradientOrigin="0.25 0.25"> <RadialGradientBrush.GradientStops> <GradientStop Color="White" Offset="0"/> <GradientStop Color="Black" Offset="1"/> </RadialGradientBrush.GradientStops> </RadialGradientBrush> </Ellipse.Fill> </Ellipse> </Canvas>
One other feature of linear and radial gradients is the capability to specify the behavior when the display position maps to some position outside the range of the gradient line. The SpreadMethod property defines that behavior. The Pad mode repeats the closest point when off the line, the Reflect mode mirrors to a point on the line, and the Repeat mode simply takes the position modulo the length of the line as shown in Figure 3.21.
Figure 3.21 SpreadMethod example
Image Brushes
The role of the image brush is to map a screen position to a pixel in the specified image. For example, the following XAML would result in the image brush rendering shown in Figure 3.22.
Figure 3.22 ImageBrush example
<Canvas xmlns="http://schemas.microsoft.com/client/2007"> <Ellipse Width="450" Height="450" Stroke="Black" StrokeThickness="10" > <Ellipse.Fill> <ImageBrush ImageSource="silverlight.png"/> </Ellipse.Fill> </Ellipse> </Canvas>
Strokes
The previous section showed how to use a brush to color the fill of a shape. You can also use a brush to add color to the outline of a shape by setting the stroke properties. For example, the following XAML generates the output shown in Figure 3.23.
Figure 3.23 Sample stroke applied to an ellipse
<Canvas xmlns="http://schemas.microsoft.com/client/2007"> <Ellipse Stroke="Black" StrokeThickness="10" Canvas.Left="50" Canvas.Top="50" Width="400" Height="400" /> </Canvas>
Stroke
A stroke transforms geometry to a widened form that describes the shape outline instead of the shape fill. Silverlight fills the widened geometry with exactly the same rendering rules as the main shape fill. For example, Figure 3.24 shows an example of a widened ellipse.
Figure 3.24 The widening process applied to an ellipse
The widening process expands the original geometry by half the stroke thickness to form an outer outline. The widening process also shrinks the original geometry by half the stroke thickness to form an inner outline. The outer and inner outlines combine to form two figures Silverlight fills to produce the resulting stroke.
Figure 3.25 The widening process applied to a triangle
Dashes
To add dashes to your strokes, specify an array of distances alternating between the dash filled distance and the gap distance. For example, the simple dash array in the following XAML generates the output shown in Figure 3.26.
Figure 3.26 StrokeDashArray example of long and short dashes
<Canvas xmlns="http://schemas.microsoft.com/client/2007"> <Ellipse Stroke="Black" StrokeThickness="10" StrokeDashArray="5, 4, 2, 4" Canvas.Left="50" Canvas.Top="50" Width="400" Height="400" /> </Canvas>
Canvas
Every example shown so far has had a single root Canvas element with a set of Shape elements contained within it. In addition to providing a convenient container, the Canvas element also enables you to modify the rendering primitives it contains as a group. In particular, the Canvas element enables the following:
- Naming groups of elements
- Grouping shapes so that you can add or remove the group with a single operation
- Applying a transform to the group of elements
- Clipping a group of elements
- Apply an opacity or opacity mask effect to a group of elements
Transforms, clipping, and opacity effects are available on both individual shapes and the Canvas element.
Transforms
A transform enables you to position, rotate, scale, or skew a shape or group of shapes. To transform a group of primitives, you can set the RenderTransform on the Canvas element as exemplified in the following listing to achieve the result shown in Figure 3.27.
Figure 3.27 RenderTransform example of overlapping a rectangle over an ellipse
<Canvas xmlns="http://schemas.microsoft.com/client/2007"> <Canvas.RenderTransform> <TransformGroup> <ScaleTransform ScaleX="1.5"/> <RotateTransform Angle="30"/> <TranslateTransform X="100" Y="-10"/> </TransformGroup> </Canvas.RenderTransform> <Ellipse Fill="LightGray" Stroke="Black" StrokeThickness="20" Width="200" Height="200" /> <Rectangle Fill="Gray" Stroke="Black" StrokeThickness="20" Canvas.Left="100" Canvas.Top="100" Width="200" Height="200" /> </Canvas>
As shown in the previous example, you can use a list of ScaleTransform, TranslateTransform, and RotateTransform elements in a TransformGroup element. Alternatively, you can specify an explicit matrix with a MatrixTransform:
<Canvas xmlns="http://schemas.microsoft.com/client/2007"> <Canvas.RenderTransform> <TransformGroup> <MatrixTransform Matrix=" 1.30, 0.75, -0.50, 0.87, 100.00, -10.00" /> </TransformGroup> </Canvas.RenderTransform> <Ellipse Fill="LightGray" Stroke="Black" StrokeThickness="20" Width="200" Height="200" /> <Rectangle Fill="Gray" Stroke="Black" StrokeThickness="20" Canvas.Left="100" Canvas.Top="100" Width="200" Height="200" /> </Canvas>
3D Transforms (New in Silverlight 3)
In Silverlight 3, you can set the Projection property to a PlaneProjection to rotate a group of elements in 3D as shown in Figure 3.28.
Figure 3.28 3D projection example
<Canvas xmlns="http://schemas.microsoft.com/client/2007"> <Canvas.Projection> <PlaneProjection RotationY="-60" CenterOfRotationY="50" /> </Canvas.Projection> <Ellipse Fill="LightGray" Stroke="Black" StrokeThickness="20" Width="200" Height="200" Canvas.Top="50" /> <Rectangle Fill="Gray" Stroke="Black" StrokeThickness="20" Canvas.Left="100" Canvas.Top="100" Width="200" Height="200" /> </Canvas>
Each projection logically has its own camera. To position more than one object relative to the same perspective camera, position them all in the same place and use the GlobalOffsetX, GlobalOffsetY, and GlobalOffsetZ properties to move in the 3D world as shown in Figure 3.29.
Figure 3.29 Position three rectangles in the same 3D projection camera
<Canvas xmlns="http://schemas.microsoft.com/client/2007"> <Rectangle Fill="Gray" Stroke="Black" StrokeThickness="20" Canvas.Left="200" Canvas.Top="100" Width="200" Height="200" > <Rectangle.Projection> <PlaneProjection GlobalOffsetX="-200" RotationY="-60" CenterOfRotationY="50" /> </Rectangle.Projection> </Rectangle> <Rectangle Fill="Gray" Stroke="Black" StrokeThickness="20" Canvas.Left="200" Canvas.Top="100" Width="200" Height="200" > <Rectangle.Projection> <PlaneProjection GlobalOffsetZ="-150"/> </Rectangle.Projection> </Rectangle> <Rectangle Fill="Gray" Stroke="Black" StrokeThickness="20" Canvas.Left="200" Canvas.Top="100" Width="200" Height="200" > <Rectangle.Projection> <PlaneProjection GlobalOffsetX="200" RotationY="60" CenterOfRotationY="50" /> </Rectangle.Projection> </Rectangle> </Canvas>
The global offset properties apply after the rotation property. You can also use the LocalOffsetX, LocalOffsetY, and LocalOffsetZ properties on the PlaneProjection object to apply an offset before the rotation.
Clipping
Clipping is the process of restricting the display area to a specified shape. To clip an element, set the Clip property as shown Figure 3.30 and in the following listing:
<Canvas xmlns="http://schemas.microsoft.com/client/2007"> <Canvas.Clip> <EllipseGeometry Center="100,200" RadiusX="150" RadiusY="150" /> </Canvas.Clip> <Ellipse Fill="LightGray" Stroke="Black" StrokeThickness="20" Width="200" Height="200" /> <Rectangle Fill="Gray" Stroke="Black" StrokeThickness="20" Canvas.Left="100" Canvas.Top="100" Width="200" Height="200" /> </Canvas>
Figure 3.30 Clipping example
Opacity
Setting opacity on a brush or setting a transparent color on a brush introduces alpha blending. In particular, if a brush contains a transparent color, the brush blends its color with the content underneath using the following formula:
Color_destination = alpha * Color_source + (1 – alpha) * Color_destination
The other form of opacity is setting the Opacity property on a Canvas. This operation is not equivalent to changing the opacity of each of the shapes within the Canvas element as demonstrated by Figure 3.31.
Figure 3.31 Canvas Opacity versus per path Opacity
OpacityMask
The OpacityMask property on a UIElement provides a mechanism to blend brush per pixel alpha information with the content of a UIElement. For example, the following XAML would produce the result shown in Figure 3.32.
Figure 3.32 OpacityMask example
<Canvas xmlns="http://schemas.microsoft.com/client/2007"> <Canvas.OpacityMask> <LinearGradientBrush StartPoint="0,0" EndPoint="1,1"> <LinearGradientBrush.GradientStops> <GradientStop Color="Transparent" Offset="0"/> <GradientStop Color="White" Offset="1"/> </LinearGradientBrush.GradientStops> </LinearGradientBrush> </Canvas.OpacityMask> <Ellipse Fill="LightGray" Stroke="Black" StrokeThickness="20" Width="200" Height="200" /> <Rectangle Fill="Gray" Stroke="Black" StrokeThickness="20" Canvas.Left="100" Canvas.Top="100" Width="200" Height="200" /> </Canvas>
OpacityMask is slow at runtime. In some cases, it is faster to draw content on top that blends to the background instead of using the OpacityMask. For example, you can achieve the effect in Figure 3.32 with the following XAML:
<Canvas xmlns="http://schemas.microsoft.com/client/2007"> <Ellipse Fill="LightGray" Stroke="Black" StrokeThickness="20" Width="200" Height="200" /> <Rectangle Fill="Gray" Stroke="Black" StrokeThickness="20" Canvas.Left="100" Canvas.Top="100" Width="200" Height="200" /> <!–– simulate opacity mask effect with a rectangle on top ––> <Rectangle Width="300" Height="300"> <Rectangle.Fill> <LinearGradientBrush StartPoint="0,0" EndPoint="1,1"> <LinearGradientBrush.GradientStops> <GradientStop Color="White" Offset="0"/> <GradientStop Color="Transparent" Offset="1"/> </LinearGradientBrush.GradientStops> </LinearGradientBrush> </Rectangle.Fill> </Rectangle> </Canvas>