Friday, October 23, 2009

Silverlight Eyecandy - Spawning A Random TextBlock

We will cover Storyboard Animations, Loading Data from Dynamic XML Files, and Using the Silverlight Random Class

I will show you how to load a list of words from a dynamic XML file, and display those words on a canvas at random sizes and using random fonts just like the Silverlight application below.




If the above silverlight app does not show up for you, try clicking on the link of the topic, the pathing got screwy with Blogger no longer allowing publishing to ftp.

For this project you will need to Add Reference To System.Xml.Linq


Step 1.
Start by creating a new Silverlight Application project in Visual Studio

Step 2.
Inside your LayoutRoot Grid, create a Canvas & inside that Canvas create a Textblock. Created a Loaded callback for the Canvas. Set Opacity to 0.

<Usercontrol x:Class="CanvasTextblock_EyeCandy.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
    mc:Ignorable="d" d:DesignWidth="640" d:DesignHeight="480">
  <Grid x:Name="LayoutRoot">
        <Canvas x:Name="canvas1" Background="#303030" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Loaded="Canvas_Loaded" >
            <TextBlock x:Name="textBlock1" Opacity="0" Foreground="WhiteSmoke" />
        </Canvas>
    </Grid>
</UserControl>

Step 3.
Create storyboard in the UserControl resources for fading the Textblock's Opacity in and out using a DoubleAnimation. We'll set To to 0.85, Duration to 3 seconds. Also, set AutoReverse to True and then create a callback for the DoubleAnimation being Completed

Then create 2 more DoubleAnimations to change the Canvas.Top and Canvas.Left attributes of the TextBlock, set their To to 1

Your code should look something like this:
<UserControl x:Class="CanvasTextblock_EyeCandy.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
    mc:Ignorable="d" d:DesignWidth="640" d:DesignHeight="480" >
    <UserControl.Resources>
        <Storyboard x:Name="textBlockFader">
            <DoubleAnimation  Storyboard.TargetName="textBlock1" Storyboard.TargetProperty="(Canvas.Top)" To="1" Duration="00:00:00" x:Name="tbPositionTop" />
            <DoubleAnimation  Storyboard.TargetName="textBlock1" Storyboard.TargetProperty="(Canvas.Left)" To="1" Duration="00:00:00" x:Name="tbPositionLeft" />
            <DoubleAnimation Storyboard.TargetName="textBlock1" Storyboard.TargetProperty="Opacity" To="0.85" AutoReverse="True" Duration="00:00:03" Completed="DoubleAnimation_Completed" />
        </Storyboard>
    </UserControl.Resources>
  <Grid x:Name="LayoutRoot">
        <Canvas x:Name="canvas1" Background="#303030" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Loaded="Canvas_Loaded" >
            <TextBlock x:Name="textBlock1" Opacity="0" Foreground="WhiteSmoke" />
        </Canvas>
    </Grid>
</UserControl>

Step 4.
Now we create the XML file that will store our Words, Fonts, and FontSizes we'll be using for our randomness. You can either use my XML file for your sample or the code below. For my example, if the webpage hosting my silverlight control is at http://domain.com/index.htm then my XML file is going to be in http://domain.com/Data/SmileWords.xml :

<?xml version="1.0" encoding="utf-8" ?>
<Data>
  <Words>
    <Word>Smile</Word>
    <Word>Smirk</Word>
    <Word>Grin</Word>
    <Word>Whiter Teeth</Word>
    <Word>Laugh</Word>
    <Word>Laughter</Word>
    <Word>Happiness</Word>
    <Word>Success</Word>
    <Word>Straight Teeth</Word>
    <Word>Beam</Word>
    <Word>Joy</Word>
    <Word>Joyful</Word>
    <Word>Happy</Word>
    <Word>Shine</Word>
  </Words>
  <Sizes>
    <Size>18</Size>
    <Size>20</Size>
    <Size>24</Size>
    <Size>28</Size>
    <Size>30</Size>
    <Size>32</Size>
    <Size>36</Size>
    <Size>40</Size>
    <Size>44</Size>
    <Size>48</Size>
    <Size>50</Size>
    <Size>54</Size>
    <Size>58</Size>
    <Size>60</Size>
    <Size>66</Size>
    <Size>72</Size>    
  </Sizes>
  <Fonts>
    <Font>Arial</Font>
    <Font>Arial Black</Font>
    <Font>Calibri</Font>
    <Font>Cambria</Font>
    <Font>Cambria Math</Font>
    <Font>Comic Sans MS</Font>
    <Font>Candara</Font>
    <Font>Consolas</Font>
    <Font>Constantia</Font>
    <Font>Corbel</Font>
    <Font>Courier New</Font>
    <Font>Georgia</Font>
    <Font>Tahoma</Font>
    <Font>Times New Roman</Font>
    <Font>Trebuchet MS</Font>
    <Font>Verdana</Font>
  </Fonts>
</Data>

Step 5.
We'll now go to the code-behind. You'll need to add 3 code references that are not added to your project by default:

using System.Xml;
using System.Xml.Linq;
using System.IO;

You'll also want to make 3 List<> declarations to store the values we load from our dynamic XML file and declare an instance of the Silverlight Random class:
List<string> words = new List<string>();
List<string> fontnames = new List<string>();
List<double> fontsizes = new List<double>();
Random myrand = new Random();

Inside the DoubleAnimation_Completed callback we have the code for setting up the next randomization:
private void DoubleAnimation_Completed(object sender, EventArgs e)
{
    textBlock1.Text = words[myrand.Next(0, words.Count - 1)];
    textBlock1.FontFamily = new FontFamily(fontnames[myrand.Next(0, fontnames.Count - 1)]);
    textBlock1.FontSize = fontsizes[myrand.Next(0, fontsizes.Count - 1)];
    tbPositionTop.To = myrand.Next(1, Convert.ToInt32(canvas1.ActualHeight - textBlock1.ActualHeight));
    tbPositionLeft.To = myrand.Next(1, Convert.ToInt32(canvas1.ActualWidth - textBlock1.ActualWidth));
    textBlockFader.Begin();
}

Then in the Loaded function for the Canvas you're going to download the XML file for loading.

private void Canvas_Loaded(object sender, RoutedEventArgs e)
{
    WebClient client = new WebClient();
    client.DownloadStringCompleted += new DownloadStringCompletedEventHandler(client_DownloadStringCompleted);
    Uri url = new Uri("/Data/SmileWords.xml", UriKind.RelativeOrAbsolute);
    client.DownloadStringAsync(url);
}
In the callback for DownloadStringCompleted:
void client_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
{
     StringReader stream = new StringReader(e.Result);
     XmlReader reader = XmlReader.Create(stream);
     XDocument xmlDoc = XDocument.Load(reader);
     words = (from at in xmlDoc.Descendants("Words").Descendants("Word")
              select at.Value.ToString()).ToList();
     fontsizes = (from at in xmlDoc.Descendants("Sizes").Descendants("Size")
              select Convert.ToDouble(at.Value)).ToList();
     fontnames = (from at in xmlDoc.Descendants("Fonts").Descendants("Font")
              select at.Value.ToString()).ToList();
     DoubleAnimation_Completed(null, null);
}


That's it! Your program should now download the XML file upon loading, parse and store the information into memory, and begin the eyecandy!

You can further customize the randomness by giving a seperate Random class for each attribute you're randomizing, and initializing the Random class each with a different seed. You could also add in FontWeights, RenderTransforms, or even import custom Fonts.

The solution for this project can be found here: Silverlight_EC1.zip

No comments :