Home
IMPLEMENTATION OF A SILVERLIGHT APPLET

Let's have a look at the implementation of the "Almost Square" Silverlight applet. 

First, open the applet here on a separate page. If you see something like the image below
                                      
                                      Install Silverlight

then you do not have a Silverlight viewer installed.  You can install one in less than a minute by double-clicking on the Install  image. 

Otherwise, you should see a black panel with the inscription, "What's almost square and has a bumpy surface and is surrounded by water and is cool?" and clickng on the button should take you to the answer: a 3-D relief map of San Francisco, which is almost a square with 7-mile edges, is decidedly bumpy, is at the tip of a narrow peninsula nearly surrounded by water with a mild climate which is on the chilly side most of the year.  The map should  zoom into view through a 3/4 turn coming to rest looking like the reduced scale view below.

                           Static image (reduced)

(For the record, the author created the map in 2002 from GIS data supplied by the USGS.)

The applet is implemented in two files: a file with declarative syntax, that is mark-up code, and a C# code-behind file.   The mark-up causes most of the implementation.  Listing 1 shows this file, Page.xaml.  Listing 2 shows the C# code behind this file.

  Listing 1- Page.xaml   (Show Code ...)

Page XAML Listing
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
 <UserControl x:Class="AlmostSquare.Page"
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     Width="1200" Height="768">
    
     <UserControl.Resources>
 
 
         <Storyboard  x:Name="theStory"  SpeedRatio="1.5">
 
             <DoubleAnimation Storyboard.TargetName="bordercontainer"
                                 Storyboard.TargetProperty="Opacity"
                                 From="0.2" To="1" Duration="0:0:2.5"></DoubleAnimation>
 
             <DoubleAnimation Storyboard.TargetName="bordercontainer"
                                 Storyboard.TargetProperty="Border.RenderTransform.Children[1].Angle"
                                 From="70" To="0" Duration="0:0:2" ></DoubleAnimation>
 
             <DoubleAnimation Storyboard.TargetName="bordercontainer"
                                 Storyboard.TargetProperty="Border.RenderTransform.Children[0].ScaleX" 
                                 From="0" To="1" Duration="0:0:2"  SpeedRatio="1" ></DoubleAnimation>
 
             <DoubleAnimation Storyboard.TargetName="bordercontainer"
                                 Storyboard.TargetProperty="Border.RenderTransform.Children[0].ScaleY" 
                                 From="0" To="1" Duration="0:0:2" SpeedRatio="1"></DoubleAnimation>
 
         </Storyboard>
 
     </UserControl.Resources>
 
 
 
     <Grid x:Name="LayoutRoot" Background="White">
         <Grid.ColumnDefinitions>
             <ColumnDefinition Width="905"/>
             <ColumnDefinition Width="380" />
 
         </Grid.ColumnDefinitions>
 
         <Border  Name="bordercontainer" Margin="1" Background="White"
                BorderBrush="DarkBlue" BorderThickness="2" CornerRadius="5"  Grid.Column="0" >
             <Canvas  x:Name="canvasImg" Grid.Column="0"  >
                 <Image  Name="imgSF" Source="Images/sfmapweb.jpg" Width="899" 
                         Height="761" Stretch="None" />
 
             </Canvas>
 
             <Border.RenderTransform>
                 <TransformGroup>
                     <ScaleTransform></ScaleTransform>
                     <RotateTransform></RotateTransform>
                 </TransformGroup>
 
             </Border.RenderTransform>
         </Border>
 
         <Canvas Background="Black" Grid.Column="1">
 
             <TextBlock FontSize="20"
                     Margin="50,5,0,0"
                     Foreground="White" TextWrapping="Wrap" Width="150" TextAlignment="Left">
                     What's almost square and has a 
                     bumpy surface and is surrounded by water and is cool?
             </TextBlock>
 
             <StackPanel Canvas.Left="100" Canvas.Top="250">
 
                 <StackPanel.Resources>
 
                     <LinearGradientBrush x:Name="theGradientBrush" StartPoint="0.5,0" EndPoint="0.5,1">
                         <GradientStop Offset="0.0" Color="LightCyan"  />
                         <GradientStop Offset="0.14" Color="Cyan"  />
                         <GradientStop Offset="0.7" Color="Blue"  />
                     </LinearGradientBrush>
                 </StackPanel.Resources>
 
                 <Button Content="WELL?" FontSize="14" x:Name="theButton" 
                         Background="{StaticResource theGradientBrush}" 
                         Canvas.Left="100" Canvas.Top="250" Width="100" Height="50" >
 
                 </Button>
             </StackPanel>
         </Canvas>
 
 
     </Grid>
 </UserControl>
 

  Listing 2- Page.xaml.cs   (Show Code ...)

Page C# Listing
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
 using System;
 using System.Collections.Generic;
 using System.Linq;
 using System.Net;
 using System.Windows;
 using System.Windows.Controls;
 using System.Windows.Documents;
 using System.Windows.Input;
 using System.Windows.Media;
 using System.Windows.Media.Animation;
 using System.Windows.Shapes;
 
 namespace AlmostSquare
 {
     public partial class Page : UserControl
     {
         static bool alreadyVisible = false;
         
         public Page()
         {
             InitializeComponent();
             bordercontainer.Visibility = Visibility.Collapsed;
             theButton.Click += new RoutedEventHandler(theButton_Click);
             theStory.Completed += new EventHandler(story_Completed);
         }
 
         private void theButton_Click(object sender, RoutedEventArgs e)
         {
             if (alreadyVisible)
                 return;
             bordercontainer.Visibility = Visibility.Visible;
             theStory.Begin();
             alreadyVisible = true;
         }
 
         void story_Completed(object sender, EventArgs e)
         {
             var story = sender as Storyboard;
             story.Stop();
         }
     
     }
 }
 

Line 1 of the declarative code in Listing 1  creates an object of fully qualified class name AlmostSquare.Page.  In the code-behind listing, AlmostSquare is a namespace, and Page is the class which inherits from UserControl.   Note here that Page is the name we have given to the class.  This is not the same class as the Webform Page described in an .aspx file.  Silverlight objects are in a UserControl top container.  Page derives from the UserControl class.  We could also have called our class something like "AlmostSquare" which in fact we have used for the namespace.

The declarative script can be functionally divided into an "animations" section and a "layout" section.  Animations are described in <UserControl.Resources> beginning from lines 6 to 29.  Layout is described in lines 33 to 87. 

A <Storyboard> object which is a property of a control type's resources contains the animation.  Each animation (lines 11, 15, 19, and 23) here is a DoubleAnimation.  A DoubleAnimation animates the value of a Double property between a pair of targets over a period of time, the duration.  The target in the four animations used here is of type Border a container class and named bordercontainer.  This element contains an image (the 3-D relief map of San Fancisco).  The animation applied to the container affects its contents as well.  The four target properties are the container's opacity (implemented in lines 11-13), its display angle (lines 15-17), its scale size in the X or horizontal direction (lines 19-21) and in the Y direction (lines 23-25).  The target properties which affect position and size are 
named in the target's RenderTransform property in lines 48-54.   Each DoubleAnimation has From and To properties.  For opacity, these values are fractions of opacity values which vary from 0 to 1, as are the scale transform values.   The variation occurs over a duration of 0:0:2, that is, 2 seconds, or 2.5 seconds for opacity.

Note how the target properties are named for the angle and scale transforms.  Each of the properties mentions the parent container Border.  We could have also referenced bordercontainer, the specific Border.  The TransformGroup is a collection of RenderTransform objects, and the Nth member of the collection is referenced by Children[N] which has a specific property like Angle or ScaleX, also named.

"Playing" the Storyboard, which has been named theStory causes each of the DoubleAnimation sequences to occur.  The Border container target changes its opacity over 2.5 seconds.  The angle of the target is rotated from 70 degrees to zero, and the scale size of the target grows from zero to 100% in both the X and Y directions.

The layout is contained in a Grid element named LayoutRoot with two columns defined in lines 35-36.  

The Border has a Canvas object containing our image, the 3-D map. 

The other column also has a Canvas with black background and a TextBlock and StackPanel objects.  The StackPanel has a button which uses its StackPanel container's Resources to set its background.  A very "WPF" appearing LinearGradientBrush achieves this effect nearly effortlessly. 

All of the previous could have been implemented programmatically.  But our VS2008 designer (or Expression Blend) permits us to see the UI as we proceed. 

Now let's consider the code-behind in Listing 2.

In the constructor, the Border container Visibility property is initialized Collapsed, that is, invisible, and a button click event handler theButton_Click is registered.

When the user presses the button, the handler checks alreadyVisible a static boolean declared at class level.  If the variable is true, then control exits from the handler.  Otherwise, the Border container visibility is set, and theStory, the name of the StoryBoard in the declarative code begins.  The static boolean is set so that we won't execute this branch again.

That's it! 

There is one other little detail... we've included an event handler that runs when the Storyboard timeline has completed.  This returns the Storyboard to a known state and signals that we should shut down anything that was initialized for the timeline. 
Home