<?xml version="1.0" encoding="utf-8"?>
<rss xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:pingback="http://madskills.com/public/xml/rss/module/pingback/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:dc="http://purl.org/dc/elements/1.1/" version="2.0">
  <channel>
    <title>Grant Archibald - silverlight</title>
    <link>http://www.garchibald.com/blog/</link>
    <description>Continuous Improvement</description>
    <language>en-us</language>
    <copyright>Grant Archibald</copyright>
    <lastBuildDate>Mon, 23 Feb 2009 13:09:47 GMT</lastBuildDate>
    <generator>newtelligence dasBlog 2.1.8102.813</generator>
    <managingEditor>http://kontactr.com/user/garchibald</managingEditor>
    <webMaster>http://kontactr.com/user/garchibald</webMaster>
    <item>
      <trackback:ping>http://www.garchibald.com/blog/Trackback.aspx?guid=cf584be9-7fa0-4489-89b2-406200309688</trackback:ping>
      <pingback:server>http://www.garchibald.com/blog/pingback.aspx</pingback:server>
      <pingback:target>http://www.garchibald.com/blog/PermaLink,guid,cf584be9-7fa0-4489-89b2-406200309688.aspx</pingback:target>
      <dc:creator>Grant Archibald</dc:creator>
      <wfw:comment>http://www.garchibald.com/blog/CommentView,guid,cf584be9-7fa0-4489-89b2-406200309688.aspx</wfw:comment>
      <wfw:commentRss>http://www.garchibald.com/blog/SyndicationService.asmx/GetEntryCommentsRss?guid=cf584be9-7fa0-4489-89b2-406200309688</wfw:commentRss>
      <body xmlns="http://www.w3.org/1999/xhtml">
        <p>
Keeping with the MIX09 contests theme, I have submitted an entry for Adam Kinney’s <a href="http://adamkinney.com/blog/411/default.aspx">Tell
a Fairy tale in Silverlight or WPF and win a FREE PASS TO MIX</a>.
</p>
        <p>
The interactive Silverlight e-book version of the Three Little Pigs is available from
my site on <a title="http://www.garchibald.com/ebooks/The-Three-Little-Pigs/" href="http://www.garchibald.com/ebooks/The-Three-Little-Pigs/">http://www.garchibald.com/ebooks/The-Three-Little-Pigs/</a><br /><br />
The e-book make use of:
</p>
        <ul>
          <li>
Page flip animations to navigate the pages.</li>
          <li>
Text to speech to read the story.</li>
          <li>
JavaScript to ensure that pages can be bookmarked. 
</li>
        </ul>
        <p>
If the user does not have Silverlight installed a HTML description is rendered and
a brief demonstration video is available to show the user what is available.
</p>
        <img width="0" height="0" src="http://www.garchibald.com/blog/aggbug.ashx?id=cf584be9-7fa0-4489-89b2-406200309688" />
      </body>
      <title>Three Little Pigs – Silverlight E-Book</title>
      <guid isPermaLink="false">http://www.garchibald.com/blog/PermaLink,guid,cf584be9-7fa0-4489-89b2-406200309688.aspx</guid>
      <link>http://www.garchibald.com/blog/2009/02/23/ThreeLittlePigsSilverlightEBook.aspx</link>
      <pubDate>Mon, 23 Feb 2009 13:09:47 GMT</pubDate>
      <description>&lt;p&gt;
Keeping with the MIX09 contests theme, I have submitted an entry for Adam Kinney’s &lt;a href="http://adamkinney.com/blog/411/default.aspx"&gt;Tell
a Fairy tale in Silverlight or WPF and win a FREE PASS TO MIX&lt;/a&gt;.
&lt;/p&gt;
&lt;p&gt;
The interactive Silverlight e-book version of the Three Little Pigs is available from
my site on &lt;a title="http://www.garchibald.com/ebooks/The-Three-Little-Pigs/" href="http://www.garchibald.com/ebooks/The-Three-Little-Pigs/"&gt;http://www.garchibald.com/ebooks/The-Three-Little-Pigs/&lt;/a&gt;
&lt;br&gt;
&lt;br&gt;
The e-book make use of:
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
Page flip animations to navigate the pages.&lt;/li&gt;
&lt;li&gt;
Text to speech to read the story.&lt;/li&gt;
&lt;li&gt;
JavaScript to ensure that pages can be bookmarked. 
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
If the user does not have Silverlight installed a HTML description is rendered and
a brief demonstration video is available to show the user what is available.
&lt;/p&gt;
&lt;img width="0" height="0" src="http://www.garchibald.com/blog/aggbug.ashx?id=cf584be9-7fa0-4489-89b2-406200309688" /&gt;</description>
      <comments>http://www.garchibald.com/blog/CommentView,guid,cf584be9-7fa0-4489-89b2-406200309688.aspx</comments>
      <category>mix09</category>
      <category>silverlight</category>
    </item>
    <item>
      <trackback:ping>http://www.garchibald.com/blog/Trackback.aspx?guid=979ea140-b656-4106-897d-2fb62eacff0b</trackback:ping>
      <pingback:server>http://www.garchibald.com/blog/pingback.aspx</pingback:server>
      <pingback:target>http://www.garchibald.com/blog/PermaLink,guid,979ea140-b656-4106-897d-2fb62eacff0b.aspx</pingback:target>
      <dc:creator>Grant Archibald</dc:creator>
      <wfw:comment>http://www.garchibald.com/blog/CommentView,guid,979ea140-b656-4106-897d-2fb62eacff0b.aspx</wfw:comment>
      <wfw:commentRss>http://www.garchibald.com/blog/SyndicationService.asmx/GetEntryCommentsRss?guid=979ea140-b656-4106-897d-2fb62eacff0b</wfw:commentRss>
      <body xmlns="http://www.w3.org/1999/xhtml">
        <p>
Inspired by Mix09 being held in Las Vegas the logical choice of entry was a slot machine
game. So my Silverlight entry Spin &amp; Win was created.
</p>
        <p>
          <a href="http://2009.visitmix.com/MIXtify/TenKDisplay.aspx?SubmissionID=0094%20">
            <img src="http://www.garchibald.com/images/Mix09-10k/FullScreenShot.jpg" alt="Spin And Win Screenshot" />
          </a>
        </p>
        <p>
You can play the game online at the <a href="http://2009.visitmix.com/MIXtify/TenKDisplay.aspx?SubmissionID=0094%20">Mix09
Site</a> and information about the game is linked within the game and is also available
from my <a href="http://www.garchibald.com/mix09_10k">Mix09 10k Information/Help page</a>.
</p>
        <p>
Keeping the entry inside the 10k limit was a definite challenge. Overall it was a
great process, diving deeper into creating a Silverlight game encompassing a whole
range of factors to create the final product:
</p>
        <ul>
          <li>
Animation of controls to create visual effects. 
</li>
          <li>
Coding optimisation techniques to minimise the size of the code. 
</li>
          <li>
Dynamically loading controls and components. 
</li>
          <li>
External links from the application e.g. Social bookmarks and Information/Help on
the game. 
</li>
          <li>
Graphic design of the game. 
</li>
          <li>
Interaction with external services. 
</li>
          <li>
State management giving the ability to save and load the state of the game.</li>
        </ul>
        <p>
Visit the  Mix site now and <a href="http://2009.visitmix.com/MIXtify/TenKDisplay.aspx?SubmissionID=0094">add
a vote</a> and I look forward to your comments.
</p>
        <img width="0" height="0" src="http://www.garchibald.com/blog/aggbug.ashx?id=979ea140-b656-4106-897d-2fb62eacff0b" />
      </body>
      <title>My Mix09 10k Contest Entry Is Live – Spin And Win</title>
      <guid isPermaLink="false">http://www.garchibald.com/blog/PermaLink,guid,979ea140-b656-4106-897d-2fb62eacff0b.aspx</guid>
      <link>http://www.garchibald.com/blog/2009/01/23/MyMix0910kContestEntryIsLiveSpinAndWin.aspx</link>
      <pubDate>Fri, 23 Jan 2009 10:02:30 GMT</pubDate>
      <description>&lt;p&gt;
Inspired by Mix09 being held in Las Vegas the logical choice of entry was a slot machine
game. So my Silverlight entry Spin &amp;amp; Win was created.
&lt;/p&gt;
&lt;p&gt;
&lt;a href="http://2009.visitmix.com/MIXtify/TenKDisplay.aspx?SubmissionID=0094%20"&gt;&lt;img src="http://www.garchibald.com/images/Mix09-10k/FullScreenShot.jpg" alt="Spin And Win Screenshot"&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;
You can play the game online at the &lt;a href="http://2009.visitmix.com/MIXtify/TenKDisplay.aspx?SubmissionID=0094%20"&gt;Mix09
Site&lt;/a&gt; and information about the game is linked within the game and is also available
from my &lt;a href="http://www.garchibald.com/mix09_10k"&gt;Mix09 10k Information/Help page&lt;/a&gt;.
&lt;/p&gt;
&lt;p&gt;
Keeping the entry inside the 10k limit was a definite challenge. Overall it was a
great process, diving deeper into creating a Silverlight game encompassing a whole
range of factors to create the final product:
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
Animation of controls to create visual effects. 
&lt;/li&gt;
&lt;li&gt;
Coding optimisation techniques to minimise the size of the code. 
&lt;/li&gt;
&lt;li&gt;
Dynamically loading controls and components. 
&lt;/li&gt;
&lt;li&gt;
External links from the application e.g. Social bookmarks and Information/Help on
the game. 
&lt;/li&gt;
&lt;li&gt;
Graphic design of the game. 
&lt;/li&gt;
&lt;li&gt;
Interaction with external services. 
&lt;/li&gt;
&lt;li&gt;
State management giving the ability to save and load the state of the game.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
Visit the&amp;nbsp; Mix site now and &lt;a href="http://2009.visitmix.com/MIXtify/TenKDisplay.aspx?SubmissionID=0094"&gt;add
a vote&lt;/a&gt; and I look forward to your comments.
&lt;/p&gt;
&lt;img width="0" height="0" src="http://www.garchibald.com/blog/aggbug.ashx?id=979ea140-b656-4106-897d-2fb62eacff0b" /&gt;</description>
      <comments>http://www.garchibald.com/blog/CommentView,guid,979ea140-b656-4106-897d-2fb62eacff0b.aspx</comments>
      <category>mix09</category>
      <category>silverlight</category>
    </item>
    <item>
      <trackback:ping>http://www.garchibald.com/blog/Trackback.aspx?guid=93e64dfe-4561-4208-b315-60e86b62a265</trackback:ping>
      <pingback:server>http://www.garchibald.com/blog/pingback.aspx</pingback:server>
      <pingback:target>http://www.garchibald.com/blog/PermaLink,guid,93e64dfe-4561-4208-b315-60e86b62a265.aspx</pingback:target>
      <dc:creator>Grant Archibald</dc:creator>
      <wfw:comment>http://www.garchibald.com/blog/CommentView,guid,93e64dfe-4561-4208-b315-60e86b62a265.aspx</wfw:comment>
      <wfw:commentRss>http://www.garchibald.com/blog/SyndicationService.asmx/GetEntryCommentsRss?guid=93e64dfe-4561-4208-b315-60e86b62a265</wfw:commentRss>
      <body xmlns="http://www.w3.org/1999/xhtml">
        <h2>Overview
</h2>
        <p>
In this post I will provide an overview of the changes from the original wav media
files, to wma files required by the Silverlight version. 
<br /></p>
        <p>
The main sections are:
</p>
        <ul>
          <li>
            <a href="#Introduction">Introduction</a> – Discusses the background behind the use
of audio files in the original WPF version and an overview of the changes required. 
</li>
          <li>
            <a href="#CodeChanges">Code Changes</a> – Discusses the changes required to move from
win32 api implementation to the MediaElement. 
</li>
          <li>
            <a href="#AudioFileChanges">Audio File Changes</a> – Discusses uses Microsoft Expression
Media Encoder 2 to convert the wav files to wma files. 
</li>
          <li>
            <a href="#Summary">Summary</a> – Overview of the changes made and the advantages of
the change. 
<br /></li>
        </ul>
        <h2>
          <a name="Introduction">Introduction</a>
        </h2>
        <p>
As Scott indicated in his first post <a href="http://www.hanselman.com/blog/IntroducingBabySmashAWPFExperiment.aspx">Introducing
BabySmash</a>, he started using techniques that he already knew. As a result the laughs
and giggles played when a key is pressed where included as embedded wav files. When
moving to the Silverlight environment this approach require some modifications to
optimise the download experience and cater for the differences of running cross platform
within a browser. 
<br /></p>
        <h2>
          <a name="CodeChanges">Code Changes</a>
        </h2>
        <p>
The original audio files used by the WPF application where wav files as in made use
of class called Win32Audio, which wrapped calls to the underlying Windows unmanaged
win32 api’s. 
</p>
        <p>
        </p>
        <div style="margin: 0px; padding: 0px; display: inline; float: none;" id="scid:812469c5-0cb0-4c63-8c15-c81123a09de7:e767aeeb-2773-44b9-b0db-60eb0bac08ec" class="wlWriterEditableSmartContent">
          <pre name="code" class="c#">    public class Win32Audio
    {
        #region WIN32 Const Definitions
        public const UInt32 SND_ASYNC = 0x0001;
        public const UInt32 SND_LOOP = 0x0008;
        public const UInt32 SND_MEMORY = 0x0004;
        public const UInt32 SND_NOSTOP = 0x0010;
        #endregion

        // this is the overload we want to play embedded resource...
        public static Dictionary&lt;string, string&gt; cachedWavs = new Dictionary&lt;string, string&gt;();
        public static object cachedWavsLock = new object();

        [DllImport("winmm.dll")]
        public static extern bool PlaySound(byte[] data, IntPtr hMod, UInt32 dwFlags);

        [DllImport("winmm.dll", SetLastError = true)]
        private static extern bool PlaySound(string pszSound, IntPtr hmod, UInt32 fdwSound);

        public void PlayWavResource(string wav)
        {
            string s = GetWavResource(wav);
            PlaySound(s, IntPtr.Zero, SND_ASYNC);
        }</pre>
        </div>
        <p>
        </p>
        <p>
For the Silverlight version pinvokes to the underlying operating system are not allowed
from  both a security point and cross platform point of view. The answer was
to implement a new class XamlAudio that makes use of the MediaElement class present
in both WPF and Silverlight to play the audio files. 
<br /></p>
        <div style="margin: 0px; padding: 0px; display: inline; float: none;" id="scid:812469c5-0cb0-4c63-8c15-c81123a09de7:9ca0baf8-22cd-46d1-861a-83d34d0db5cf" class="wlWriterEditableSmartContent">
          <pre name="code" class="c#">    public class XamlAudio
    {
        private MediaElement player;
        readonly Queue&lt;string&gt; soundsToPlay = new Queue&lt;string&gt;();
        private bool inPlay;

        private Dictionary&lt;string, Stream&gt; media = new Dictionary&lt;string, Stream&gt;();

        //TODO Add lock and config value for sound queue limit
        public void PlayWavResourceYield(string s)
        {
            if (player == null)
                return;

            if (inPlay &amp;&amp; soundsToPlay.Count &lt; 5)
            {
                soundsToPlay.Enqueue(s);
                return;
            }

            PlaySound(s);
        }</pre>
        </div>
        <p>
The second major difference in the code is the shift from  the use of embedded
audio files, as indicated in my earlier post <a href="http://www.garchibald.com/blog/2008/11/16/SilverlightBabySmashPerformanceTheAsynchronousStory.aspx">Silverlight
BabySmash Performance – The Asynchronous Story</a>, the Silverlight version asynchronously
downloads the audio files on demand. As result the initial download size for the deployed
application is smaller.
</p>
        <h2>
          <a name="AudioFileChanges">Audio File Changes</a>
          <br />
        </h2>
        <p>
The encoding format was also a major difference, changing from the use of wav files
to Window Media  Audio files (*.wma). To convert the files I used Microsoft Expression
Encoder. 
</p>
        <p>
The steps I used to convert the files where as follows: 
<br /></p>
        <ol>
          <li>
Download and install <a href="http://www.microsoft.com/expression/try-it/Default.aspx">Microsoft
Expression Encoder 2</a>. 
</li>
          <li>
Open Expression Encoder. 
</li>
          <li>
Import the existing wmv files. Either using File Import menu or by licking on the
Import button at the bottom of the window. 
</li>
          <li>
Select the Encode tab. 
</li>
          <li>
In the profile audio group expand the advanced properties. 
</li>
          <li>
Change the properties to a smaller size to optimise the smaller size. 
<ol><li>
Codec WMA 
</li><li>
Mode CBR 
</li><li>
Bitrate 20kps 
</li><li>
Sample Rate 44.1kHz 
</li><li>
Bits Per Sample 16 
</li><li>
Channels Mono</li></ol></li>
          <li>
Start the encoding process by click the encode button at the bottom of the window
or the Encode File menu item. 
</li>
          <li>
Once the encoding is complete select Output tab in the top right. 
</li>
          <li>
Open the output folder by clicking o the Open Job button in the Job Output Section.</li>
        </ol>
        <p>
From the the output folder I copied the converted wma files to the ClientBin folder
of the web site so that they where easily accessible  to the Silverlight application
when using a WebClient asynchronous download request. 
<br /></p>
        <table border="1" cellpadding="0" cellspacing="0">
          <tbody>
            <tr>
              <td width="179">
File</td>
              <td width="138">
WAV Encoding (kbps)</td>
              <td width="132">
WAV Files Size (KB)</td>
              <td width="140">
WMA Encoding (kbps)</td>
              <td width="127">
WMA File Size (KB)</td>
            </tr>
            <tr>
              <td>
babygigl2</td>
              <td>
20</td>
              <td>
6</td>
              <td>
20</td>
              <td>
15</td>
            </tr>
            <tr>
              <td>
babylaugh</td>
              <td>
88</td>
              <td>
14</td>
              <td>
20</td>
              <td>
11</td>
            </tr>
            <tr>
              <td>
ccgiggle</td>
              <td>
88</td>
              <td>
9</td>
              <td>
20</td>
              <td>
10</td>
            </tr>
            <tr>
              <td>
EditedJackPlaysBabySmash</td>
              <td>
352</td>
              <td>
227</td>
              <td>
20</td>
              <td>
25</td>
            </tr>
            <tr>
              <td>
falling</td>
              <td>
128</td>
              <td>
29</td>
              <td>
20</td>
              <td>
14</td>
            </tr>
            <tr>
              <td>
giggle</td>
              <td>
88</td>
              <td>
15</td>
              <td>
20</td>
              <td>
13</td>
            </tr>
            <tr>
              <td>
laugingmice</td>
              <td>
89</td>
              <td>
17</td>
              <td>
20</td>
              <td>
14</td>
            </tr>
            <tr>
              <td>
rising</td>
              <td>
128</td>
              <td>
26</td>
              <td>
20</td>
              <td>
13</td>
            </tr>
            <tr>
              <td>
scooby2</td>
              <td>
20</td>
              <td>
5</td>
              <td>
20</td>
              <td>
13</td>
            </tr>
            <tr>
              <td>
smallbumblebee</td>
              <td>
705</td>
              <td>
862</td>
              <td>
20</td>
              <td>
31</td>
            </tr>
          </tbody>
        </table>
        <p>
One interesting point from the comparison of the conversion is that for the small
files with the same bit rate encoding the WMA files where larger. If further optimisation
in terms of files size is required, then this is an area that could be further investigated
to determine the exact encoding settings to trade-off audio quality against file size. 
<br /></p>
        <h2>
          <a name="Summary">Summary</a>
        </h2>
        <p>
        </p>
        <ul>
          <li>
Changes to the C# code to make use of the built in MediaElement to play the audio
files. 
</li>
          <li>
Cross platform audio support using wma files instead of wmv files. 
</li>
          <li>
Use of Expression Encoder to convert the source wmv files to a web optimised encoding. 
</li>
        </ul>
        <img width="0" height="0" src="http://www.garchibald.com/blog/aggbug.ashx?id=93e64dfe-4561-4208-b315-60e86b62a265" />
      </body>
      <title>Silverlight BabySmash Audio Files</title>
      <guid isPermaLink="false">http://www.garchibald.com/blog/PermaLink,guid,93e64dfe-4561-4208-b315-60e86b62a265.aspx</guid>
      <link>http://www.garchibald.com/blog/2008/12/19/SilverlightBabySmashAudioFiles.aspx</link>
      <pubDate>Fri, 19 Dec 2008 00:11:25 GMT</pubDate>
      <description>&lt;h2&gt;Overview
&lt;/h2&gt;
&lt;p&gt;
In this post I will provide an overview of the changes from the original wav media
files, to wma files required by the Silverlight version. 
&lt;br&gt;
&lt;/p&gt;
&lt;p&gt;
The main sections are:
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;a href="#Introduction"&gt;Introduction&lt;/a&gt; – Discusses the background behind the use
of audio files in the original WPF version and an overview of the changes required. 
&lt;/li&gt;
&lt;li&gt;
&lt;a href="#CodeChanges"&gt;Code Changes&lt;/a&gt; – Discusses the changes required to move from
win32 api implementation to the MediaElement. 
&lt;/li&gt;
&lt;li&gt;
&lt;a href="#AudioFileChanges"&gt;Audio File Changes&lt;/a&gt; – Discusses uses Microsoft Expression
Media Encoder 2 to convert the wav files to wma files. 
&lt;/li&gt;
&lt;li&gt;
&lt;a href="#Summary"&gt;Summary&lt;/a&gt; – Overview of the changes made and the advantages of
the change. 
&lt;br&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;&lt;a name="Introduction"&gt;Introduction&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;
As Scott indicated in his first post &lt;a href="http://www.hanselman.com/blog/IntroducingBabySmashAWPFExperiment.aspx"&gt;Introducing
BabySmash&lt;/a&gt;, he started using techniques that he already knew. As a result the laughs
and giggles played when a key is pressed where included as embedded wav files. When
moving to the Silverlight environment this approach require some modifications to
optimise the download experience and cater for the differences of running cross platform
within a browser. 
&lt;br&gt;
&lt;/p&gt;
&lt;h2&gt;&lt;a name="CodeChanges"&gt;Code Changes&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;
The original audio files used by the WPF application where wav files as in made use
of class called Win32Audio, which wrapped calls to the underlying Windows unmanaged
win32 api’s. 
&lt;/p&gt;
&lt;p&gt;
&lt;/p&gt;
&lt;div style="margin: 0px; padding: 0px; display: inline; float: none;" id="scid:812469c5-0cb0-4c63-8c15-c81123a09de7:e767aeeb-2773-44b9-b0db-60eb0bac08ec" class="wlWriterEditableSmartContent"&gt;&lt;pre name="code" class="c#"&gt;    public class Win32Audio
    {
        #region WIN32 Const Definitions
        public const UInt32 SND_ASYNC = 0x0001;
        public const UInt32 SND_LOOP = 0x0008;
        public const UInt32 SND_MEMORY = 0x0004;
        public const UInt32 SND_NOSTOP = 0x0010;
        #endregion

        // this is the overload we want to play embedded resource...
        public static Dictionary&amp;lt;string, string&amp;gt; cachedWavs = new Dictionary&amp;lt;string, string&amp;gt;();
        public static object cachedWavsLock = new object();

        [DllImport("winmm.dll")]
        public static extern bool PlaySound(byte[] data, IntPtr hMod, UInt32 dwFlags);

        [DllImport("winmm.dll", SetLastError = true)]
        private static extern bool PlaySound(string pszSound, IntPtr hmod, UInt32 fdwSound);

        public void PlayWavResource(string wav)
        {
            string s = GetWavResource(wav);
            PlaySound(s, IntPtr.Zero, SND_ASYNC);
        }&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;
&lt;/p&gt;
&lt;p&gt;
For the Silverlight version pinvokes to the underlying operating system are not allowed
from&amp;nbsp; both a security point and cross platform point of view. The answer was
to implement a new class XamlAudio that makes use of the MediaElement class present
in both WPF and Silverlight to play the audio files. 
&lt;br&gt;
&lt;/p&gt;
&lt;div style="margin: 0px; padding: 0px; display: inline; float: none;" id="scid:812469c5-0cb0-4c63-8c15-c81123a09de7:9ca0baf8-22cd-46d1-861a-83d34d0db5cf" class="wlWriterEditableSmartContent"&gt;&lt;pre name="code" class="c#"&gt;    public class XamlAudio
    {
        private MediaElement player;
        readonly Queue&amp;lt;string&amp;gt; soundsToPlay = new Queue&amp;lt;string&amp;gt;();
        private bool inPlay;

        private Dictionary&amp;lt;string, Stream&amp;gt; media = new Dictionary&amp;lt;string, Stream&amp;gt;();

        //TODO Add lock and config value for sound queue limit
        public void PlayWavResourceYield(string s)
        {
            if (player == null)
                return;

            if (inPlay &amp;amp;&amp;amp; soundsToPlay.Count &amp;lt; 5)
            {
                soundsToPlay.Enqueue(s);
                return;
            }

            PlaySound(s);
        }&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;
The second major difference in the code is the shift from&amp;nbsp; the use of embedded
audio files, as indicated in my earlier post &lt;a href="http://www.garchibald.com/blog/2008/11/16/SilverlightBabySmashPerformanceTheAsynchronousStory.aspx"&gt;Silverlight
BabySmash Performance – The Asynchronous Story&lt;/a&gt;, the Silverlight version asynchronously
downloads the audio files on demand. As result the initial download size for the deployed
application is smaller.
&lt;/p&gt;
&lt;h2&gt;&lt;a name="AudioFileChanges"&gt;Audio File Changes&lt;/a&gt; 
&lt;br&gt;
&lt;/h2&gt;
&lt;p&gt;
The encoding format was also a major difference, changing from the use of wav files
to Window Media&amp;nbsp; Audio files (*.wma). To convert the files I used Microsoft Expression
Encoder. 
&lt;/p&gt;
&lt;p&gt;
The steps I used to convert the files where as follows: 
&lt;br&gt;
&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
Download and install &lt;a href="http://www.microsoft.com/expression/try-it/Default.aspx"&gt;Microsoft
Expression Encoder 2&lt;/a&gt;. 
&lt;/li&gt;
&lt;li&gt;
Open Expression Encoder. 
&lt;/li&gt;
&lt;li&gt;
Import the existing wmv files. Either using File Import menu or by licking on the
Import button at the bottom of the window. 
&lt;/li&gt;
&lt;li&gt;
Select the Encode tab. 
&lt;/li&gt;
&lt;li&gt;
In the profile audio group expand the advanced properties. 
&lt;/li&gt;
&lt;li&gt;
Change the properties to a smaller size to optimise the smaller size. 
&lt;ol&gt;
&lt;li&gt;
Codec WMA 
&lt;/li&gt;
&lt;li&gt;
Mode CBR 
&lt;/li&gt;
&lt;li&gt;
Bitrate 20kps 
&lt;/li&gt;
&lt;li&gt;
Sample Rate 44.1kHz 
&lt;/li&gt;
&lt;li&gt;
Bits Per Sample 16 
&lt;/li&gt;
&lt;li&gt;
Channels Mono&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
Start the encoding process by click the encode button at the bottom of the window
or the Encode File menu item. 
&lt;/li&gt;
&lt;li&gt;
Once the encoding is complete select Output tab in the top right. 
&lt;/li&gt;
&lt;li&gt;
Open the output folder by clicking o the Open Job button in the Job Output Section.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;
From the the output folder I copied the converted wma files to the ClientBin folder
of the web site so that they where easily accessible&amp;nbsp; to the Silverlight application
when using a WebClient asynchronous download request. 
&lt;br&gt;
&lt;/p&gt;
&lt;table border="1" cellpadding="0" cellspacing="0"&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td width="179"&gt;
File&lt;/td&gt;
&lt;td width="138"&gt;
WAV Encoding (kbps)&lt;/td&gt;
&lt;td width="132"&gt;
WAV Files Size (KB)&lt;/td&gt;
&lt;td width="140"&gt;
WMA Encoding (kbps)&lt;/td&gt;
&lt;td width="127"&gt;
WMA File Size (KB)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
babygigl2&lt;/td&gt;
&lt;td&gt;
20&lt;/td&gt;
&lt;td&gt;
6&lt;/td&gt;
&lt;td&gt;
20&lt;/td&gt;
&lt;td&gt;
15&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
babylaugh&lt;/td&gt;
&lt;td&gt;
88&lt;/td&gt;
&lt;td&gt;
14&lt;/td&gt;
&lt;td&gt;
20&lt;/td&gt;
&lt;td&gt;
11&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
ccgiggle&lt;/td&gt;
&lt;td&gt;
88&lt;/td&gt;
&lt;td&gt;
9&lt;/td&gt;
&lt;td&gt;
20&lt;/td&gt;
&lt;td&gt;
10&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
EditedJackPlaysBabySmash&lt;/td&gt;
&lt;td&gt;
352&lt;/td&gt;
&lt;td&gt;
227&lt;/td&gt;
&lt;td&gt;
20&lt;/td&gt;
&lt;td&gt;
25&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
falling&lt;/td&gt;
&lt;td&gt;
128&lt;/td&gt;
&lt;td&gt;
29&lt;/td&gt;
&lt;td&gt;
20&lt;/td&gt;
&lt;td&gt;
14&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
giggle&lt;/td&gt;
&lt;td&gt;
88&lt;/td&gt;
&lt;td&gt;
15&lt;/td&gt;
&lt;td&gt;
20&lt;/td&gt;
&lt;td&gt;
13&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
laugingmice&lt;/td&gt;
&lt;td&gt;
89&lt;/td&gt;
&lt;td&gt;
17&lt;/td&gt;
&lt;td&gt;
20&lt;/td&gt;
&lt;td&gt;
14&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
rising&lt;/td&gt;
&lt;td&gt;
128&lt;/td&gt;
&lt;td&gt;
26&lt;/td&gt;
&lt;td&gt;
20&lt;/td&gt;
&lt;td&gt;
13&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
scooby2&lt;/td&gt;
&lt;td&gt;
20&lt;/td&gt;
&lt;td&gt;
5&lt;/td&gt;
&lt;td&gt;
20&lt;/td&gt;
&lt;td&gt;
13&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
smallbumblebee&lt;/td&gt;
&lt;td&gt;
705&lt;/td&gt;
&lt;td&gt;
862&lt;/td&gt;
&lt;td&gt;
20&lt;/td&gt;
&lt;td&gt;
31&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;
One interesting point from the comparison of the conversion is that for the small
files with the same bit rate encoding the WMA files where larger. If further optimisation
in terms of files size is required, then this is an area that could be further investigated
to determine the exact encoding settings to trade-off audio quality against file size. 
&lt;br&gt;
&lt;/p&gt;
&lt;h2&gt;&lt;a name="Summary"&gt;Summary&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
Changes to the C# code to make use of the built in MediaElement to play the audio
files. 
&lt;/li&gt;
&lt;li&gt;
Cross platform audio support using wma files instead of wmv files. 
&lt;/li&gt;
&lt;li&gt;
Use of Expression Encoder to convert the source wmv files to a web optimised encoding. 
&lt;/li&gt;
&lt;/ul&gt;
&lt;img width="0" height="0" src="http://www.garchibald.com/blog/aggbug.ashx?id=93e64dfe-4561-4208-b315-60e86b62a265" /&gt;</description>
      <comments>http://www.garchibald.com/blog/CommentView,guid,93e64dfe-4561-4208-b315-60e86b62a265.aspx</comments>
      <category>babysmash</category>
      <category>silverlight</category>
    </item>
    <item>
      <trackback:ping>http://www.garchibald.com/blog/Trackback.aspx?guid=cacc676a-6f06-41e3-a412-aa31dbe0e74e</trackback:ping>
      <pingback:server>http://www.garchibald.com/blog/pingback.aspx</pingback:server>
      <pingback:target>http://www.garchibald.com/blog/PermaLink,guid,cacc676a-6f06-41e3-a412-aa31dbe0e74e.aspx</pingback:target>
      <dc:creator>Grant Archibald</dc:creator>
      <wfw:comment>http://www.garchibald.com/blog/CommentView,guid,cacc676a-6f06-41e3-a412-aa31dbe0e74e.aspx</wfw:comment>
      <wfw:commentRss>http://www.garchibald.com/blog/SyndicationService.asmx/GetEntryCommentsRss?guid=cacc676a-6f06-41e3-a412-aa31dbe0e74e</wfw:commentRss>
      <body xmlns="http://www.w3.org/1999/xhtml">
        <h2>Overview
</h2>
        <p>
In this blog entry I will cover some the factors effecting perceived performance and
the asynchronous code used to optimise the user experience. 
</p>
        <h2>Introduction
</h2>
        <p>
When looking at the performance of the BabySmash Silverlight application it was important
to look at a number of factors to help improve the perceived performance of the application.
These included:
</p>
        <ul>
          <li>
Initial download size. 
</li>
          <li>
Use of a secondary assembly. 
</li>
          <li>
Use of media files. 
</li>
        </ul>
        <p>
So let's start of by profiling the current application see what has been done.
</p>
        <p>
          <img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="HttpProfile" border="0" alt="Http Download Profile" src="http://www.garchibald.com/images/Silverlight-BabySmash-Performance-The-Asynchronous-Story/HttpProfile.jpg" width="584" height="509" usemap="#httpProfileMap" />
        </p>
        <map id="httpProfileMap" name="httpProfileMap">
          <area href="#InitialDownload" shape="rect" alt="Initial Download" coords="11,306,145,336" />
          <area href="#InitialDownload" shape="rect" alt="Initial Download" coords="9,384,211,410" />
          <area href="#SecondaryDownload" shape="rect" alt="Secondary Download" coords="203,309,359,340" />
          <area href="#SecondaryDownload" shape="rect" alt="Secondary Download" coords="9,409,249,434" />
          <area href="#LoadOnDemand" shape="rect" alt="Load On Demand" coords="434,310,571,337" />
          <area href="#LoadOnDemand" shape="rect" alt="Load On Demand" coords="10,433,248,500" />
        </map>
        <p>
Looking at the HTTP profile of the application there are three distinct phases:
</p>
        <ol>
          <li>
Synchronous download of the <a href="#InitialDownload">initial html code and the Silverlight
application</a>. 
</li>
          <li>
Asynchronous download of <a href="#SecondaryDownload">secondary components</a>. 
</li>
          <li>
Asynchronous download of media files <a href="#LoadOnDemand">on demand</a> as the
user presses keys.</li>
        </ol>
        <p>
          <strong>
            <a name="InitialDownload">Initial Download</a>
          </strong>
        </p>
        <p>
The default.htm file together with the initial BabySmashWeb.xap combined are around
60kb. This xap file contains all the code necessary to interact with the user, request
the additional resources and log information to the central ADO.Net Data Service.
</p>
        <p>
Using this approach the loading phase of the the Silverlight application is very short
and the user immediately shown the “Smash Canvas” as quickly a possible. 
</p>
        <div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:812469c5-0cb0-4c63-8c15-c81123a09de7:22d65f87-7d80-4eb5-bc08-843aa888e09b" class="wlWriterEditableSmartContent">
          <pre name="code" class="xml">    &lt;div id="silverlightControlHost"&gt;
        &lt;object data="data:application/x-silverlight-2," type="application/x-silverlight-2" width="100%" height="100%"&gt;
            &lt;param name="source" value="ClientBin/BabySmashWeb.xap"/&gt;
            &lt;param name="onerror" value="onSilverlightError" /&gt;
            &lt;param name="background" value="white" /&gt;
            &lt;param name="minRuntimeVersion" value="2.0.31005.0" /&gt;
            &lt;param name="autoUpgrade" value="true" /&gt;
            &lt;param name="initParams" value="logKeys=true" /&gt;
            &lt;a href="http://go.microsoft.com/fwlink/?LinkID=124807" style="text-decoration: none;"&gt;
	            &lt;img src="http://go.microsoft.com/fwlink/?LinkId=108181" alt="Get Microsoft Silverlight" style="border-style: none"/&gt;
            &lt;/a&gt;
        &lt;/object&gt;
        &lt;iframe style='visibility:hidden;height:0;width:0;border:0px'&gt;&lt;/iframe&gt;
    &lt;/div&gt;</pre>
        </div>
        <p>
        </p>
        <p>
          <strong>
            <a name="SecondaryDownload">Secondary Download</a>
          </strong>
        </p>
        <p>
Once the application is loaded, the constructor of the main code behind Page class
is called
</p>
        <div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:812469c5-0cb0-4c63-8c15-c81123a09de7:da725dfa-9c2c-4a24-b70a-275649cd0c34" class="wlWriterEditableSmartContent">
          <pre name="code" class="c#">        public Page()
        {
            InitializeComponent();

            var controller = new Controller();
            controller.Launch();
            mainPage = controller.Windows[0];

            KeyUp += new KeyEventHandler(Page_KeyUp);

            LayoutRoot.Children.Add(mainPage);
        }</pre>
        </div>
        <p>
Which calls starts a Controller class, which in turn starts the asynchronous requests
for the letters and the initial media file.
</p>
        <div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:812469c5-0cb0-4c63-8c15-c81123a09de7:76737be7-54a5-4b40-ad54-5f6f0d8e4f3f" class="wlWriterEditableSmartContent">
          <pre name="code" class="c#">       public void Launch()
       {
           CheckForUpdatesAsync();

           SetupInitialWindowState();

           //here to pre-cache letter
           CoolLetter.InitLetterStateAsync();

           //Startup sound
           audio.PlayWavResourceYield(".Resources.Sounds." + "EditedJackPlaysBabySmash.wav");
       }</pre>
        </div>
        <p>
        </p>
        <p>
        </p>
        <p>
The rest of this section will discuss how the letters stored in the secondary BabySmashEnglishLetters.xap
are downloaded and stored for use when the user presses a key. The initial technique
for loading the audio files is discussed in the implementation of PlayWavResourceYeild
in the <a href="#LoadOnDemand">Load On Demand</a> section below.
</p>
        <p>
        </p>
        <div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:812469c5-0cb0-4c63-8c15-c81123a09de7:932969bf-421c-471a-8558-bd25b5be91e1" class="wlWriterEditableSmartContent">
          <pre name="code" class="c#">   public partial class CoolLetter 
	{
      public CoolLetter()
      {
         this.InitializeComponent();
      }

	    static readonly Dictionary&lt;string, string&gt; _letters = InitLetters();

        public static void InitLetterStateAsync()
        {
            // No work to do as the private _letters will be initialised above
        }</pre>
        </div>
        <p>
        </p>
        <p>
The InitLetters method is called by the .Net Framework when the static class is initialised.
The method utilises a WebClient class instance to asynchronously download the required
secondary assembly 
</p>
        <div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:812469c5-0cb0-4c63-8c15-c81123a09de7:bff9f2e3-9797-4af9-ab96-5f74854e1d31" class="wlWriterEditableSmartContent">
          <pre name="code" class="c#">       private static Dictionary&lt;string, string&gt; InitLetters()
       {
           var addressUri = new Uri("BabySmashEnglishLetters.xap", UriKind.Relative);
           var letterLoader = new WebClient();
           letterLoader.OpenReadCompleted += letterAssemblyLoaded;
           letterLoader.OpenReadAsync(addressUri);
           return new Dictionary&lt;string, string&gt;();
       }</pre>
        </div>
        <p>
        </p>
        <p>
Once the asynchronous call is completed, reflection is used to dynamically call the
GenerateAlphanumericCharacters method of the AlphaNumericLetterGenerator class. 
</p>
        <div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:812469c5-0cb0-4c63-8c15-c81123a09de7:9c10caac-a8aa-4b39-a4e7-bdcc75f94798" class="wlWriterEditableSmartContent">
          <pre name="code" class="c#">	    private static void letterAssemblyLoaded(object sender, OpenReadCompletedEventArgs e)
	    {
	        if ((e.Error != null) || e.Cancelled) return;

	        // Convert the downloaded stream into an assembly
	        var a = LoadAssemblyFromXap("BabySmashLetters.dll", e.Result);

	        var generator = a.CreateInstance("BabySmashLetters.AlphaNumericLetterGenerator");

	        if (generator == null) return;


	        var generated = generator.GetType().InvokeMember("GenerateAlpanumericCharacters", BindingFlags.Public | BindingFlags.InvokeMethod | BindingFlags.Instance, null, generator,null) as Dictionary&lt;string, string&gt;;
            if ( generated == null ) return;
	        foreach (var pair in generated)
	        {
	            _letters.Add(pair.Key, pair.Value);
	        }
	    }
</pre>
        </div>
Loading the assembly is achieved by extracting the requested assembly from the xap
stream as follows.
<p></p><div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:812469c5-0cb0-4c63-8c15-c81123a09de7:9ae34c5c-eaa7-4c91-8d41-7aabae51e5ec" class="wlWriterEditableSmartContent"><pre name="code" class="c#">	    public static Assembly LoadAssemblyFromXap(string relativeUriString, Stream xapPackageStream)
        {
            var uri = new Uri(relativeUriString, UriKind.Relative);
            var xapPackageSri = new StreamResourceInfo(xapPackageStream, null);
            var assemblySri = Application.GetResourceStream(xapPackageSri, uri);

            var assemblyPart = new AssemblyPart();
            var a = assemblyPart.Load(assemblySri.Stream);
            return a;
        }</pre></div><p><strong><a name="LoadOnDemand">Load On Demand</a></strong></p><p>
By the time that the third stage of load on demand media files is reached the main
application is loaded, the secondary assembly is loading or loaded and the introduction
sound file has played. This has resulted in the download of around 160kb of files
and kept the time wait time for the user to a minimum.
</p><p>
Loading the external dependencies has also given us the following advantages:
</p><ul><li>
Allow for the possibility of localising the application to other non-english languages. 
</li><li>
Allow media files to be updated independently of the compiled code. 
</li><li>
Allow the media files to to be sourced from alternative locations e.g. File stored
locally on the client computer. 
</li><li>
Reduced bandwidth from 406kb for users that visit the page but do not start using
the application.</li></ul><p>
The process for loading the additional media files is similar to the process to load
the assembly secondary xap file but has the additional complexity that the user can
be pressing keys very quickly. As a result the application needs to respond very quickly
by queuing the key requests then asynchronously downloading and playing the associated
sound file.
</p><div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:812469c5-0cb0-4c63-8c15-c81123a09de7:92cbb09d-e85b-4e5e-a9b9-3acafda7b8f3" class="wlWriterEditableSmartContent"><pre name="code" class="c#">        public void PlayWavResourceYield(string s)
        {
            if (player == null)
                return;

            if (inPlay &amp;&amp; soundsToPlay.Count &lt; 5)
            {
                soundsToPlay.Enqueue(s);
                return;
            }

            PlaySound(s);
        }
</pre></div><p>
If the application is currently playing a media file then the request is placed on
queue for later playback otherwise the sound will be played. If the media file is
requested for the first time ten it will be asynchronously downloaded, added to the
available media list then played.
</p><div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:812469c5-0cb0-4c63-8c15-c81123a09de7:ebef9bbf-7910-446f-b440-e2ea6ebbb0d6" class="wlWriterEditableSmartContent"><pre name="code" class="c#">        private void PlaySound(string resourceName)
        {
            var mediaFile = GetMediaFileFromResourceName(resourceName);

            lock (media)
            {
                if (media.ContainsKey(mediaFile) == false)
                {
                    var wc = new WebClient();
                    wc.OpenReadCompleted += delegate(object sender, OpenReadCompletedEventArgs e)
                                                {
                                                    lock (media)
                                                    {
                                                        if (media.ContainsKey(mediaFile) != false) return;
                                                        media.Add(mediaFile, e.Result);
                                                        player.SetSource(e.Result);
                                                    }
                                                };
                    wc.OpenReadAsync(new Uri(mediaFile, UriKind.Relative));
                    return;
                }
            }

            var sound = media[mediaFile];
            if (sound == null) return;
            if (sound.CanSeek) sound.Position = 0;
            player.SetSource(sound);
        }</pre></div><p>
If the media file is requested again then the version available in the media list
will be reused and played to the user. 
</p><h2>Summary
</h2><p>
In summary when looking Silverlight applications one aim is to improve the perceived
user performance by:
</p><ul><li>
Quick Feedback – Provide quick feedback to the user so that the can interact with
application. Small initial xap file allowed the “Smash Canvas” and initial introduction
message where played without the overhead of other resources. 
</li><li>
Thinking Asynchronously - Performing actions asynchronously so that the user action
is not blocked while necessary processing is occurring.  Requesting media on
demand allowed content to played as needed. 
</li><li>
Separating The Application – Look at architecting the application so that it can be
split into different parts that can be managed independently if required. Using separated
code/media files allows for further expansion points and hosting options.</li></ul><img width="0" height="0" src="http://www.garchibald.com/blog/aggbug.ashx?id=cacc676a-6f06-41e3-a412-aa31dbe0e74e" /></body>
      <title>Silverlight BabySmash Performance – The Asynchronous Story</title>
      <guid isPermaLink="false">http://www.garchibald.com/blog/PermaLink,guid,cacc676a-6f06-41e3-a412-aa31dbe0e74e.aspx</guid>
      <link>http://www.garchibald.com/blog/2008/11/16/SilverlightBabySmashPerformanceTheAsynchronousStory.aspx</link>
      <pubDate>Sun, 16 Nov 2008 09:07:56 GMT</pubDate>
      <description>&lt;h2&gt;Overview
&lt;/h2&gt;
&lt;p&gt;
In this blog entry I will cover some the factors effecting perceived performance and
the asynchronous code used to optimise the user experience. 
&lt;/p&gt;
&lt;h2&gt;Introduction
&lt;/h2&gt;
&lt;p&gt;
When looking at the performance of the BabySmash Silverlight application it was important
to look at a number of factors to help improve the perceived performance of the application.
These included:
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
Initial download size. 
&lt;li&gt;
Use of a secondary assembly. 
&lt;li&gt;
Use of media files. 
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
So let's start of by profiling the current application see what has been done.
&lt;/p&gt;
&lt;p&gt;
&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="HttpProfile" border="0" alt="Http Download Profile" src="http://www.garchibald.com/images/Silverlight-BabySmash-Performance-The-Asynchronous-Story/HttpProfile.jpg" width="584" height="509" usemap="#httpProfileMap"&gt;
&lt;/p&gt;
&lt;map id="httpProfileMap" name="httpProfileMap"&gt;
&lt;area href="#InitialDownload" shape="rect" alt="Initial Download" coords="11,306,145,336"&gt;
&lt;area href="#InitialDownload" shape="rect" alt="Initial Download" coords="9,384,211,410"&gt;
&lt;area href="#SecondaryDownload" shape="rect" alt="Secondary Download" coords="203,309,359,340"&gt;
&lt;area href="#SecondaryDownload" shape="rect" alt="Secondary Download" coords="9,409,249,434"&gt;
&lt;area href="#LoadOnDemand" shape="rect" alt="Load On Demand" coords="434,310,571,337"&gt;
&lt;area href="#LoadOnDemand" shape="rect" alt="Load On Demand" coords="10,433,248,500"&gt;&lt;/map&gt; 
&lt;p&gt;
Looking at the HTTP profile of the application there are three distinct phases:
&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
Synchronous download of the &lt;a href="#InitialDownload"&gt;initial html code and the Silverlight
application&lt;/a&gt;. 
&lt;li&gt;
Asynchronous download of &lt;a href="#SecondaryDownload"&gt;secondary components&lt;/a&gt;. 
&lt;li&gt;
Asynchronous download of media files &lt;a href="#LoadOnDemand"&gt;on demand&lt;/a&gt; as the
user presses keys.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;
&lt;strong&gt;&lt;a name="InitialDownload"&gt;Initial Download&lt;/a&gt;&lt;/strong&gt;
&lt;/p&gt;
&lt;p&gt;
The default.htm file together with the initial BabySmashWeb.xap combined are around
60kb. This xap file contains all the code necessary to interact with the user, request
the additional resources and log information to the central ADO.Net Data Service.
&lt;/p&gt;
&lt;p&gt;
Using this approach the loading phase of the the Silverlight application is very short
and the user immediately shown the “Smash Canvas” as quickly a possible. 
&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:812469c5-0cb0-4c63-8c15-c81123a09de7:22d65f87-7d80-4eb5-bc08-843aa888e09b" class="wlWriterEditableSmartContent"&gt;&lt;pre name="code" class="xml"&gt;    &amp;lt;div id="silverlightControlHost"&amp;gt;
        &amp;lt;object data="data:application/x-silverlight-2," type="application/x-silverlight-2" width="100%" height="100%"&amp;gt;
            &amp;lt;param name="source" value="ClientBin/BabySmashWeb.xap"/&amp;gt;
            &amp;lt;param name="onerror" value="onSilverlightError" /&amp;gt;
            &amp;lt;param name="background" value="white" /&amp;gt;
            &amp;lt;param name="minRuntimeVersion" value="2.0.31005.0" /&amp;gt;
            &amp;lt;param name="autoUpgrade" value="true" /&amp;gt;
            &amp;lt;param name="initParams" value="logKeys=true" /&amp;gt;
            &amp;lt;a href="http://go.microsoft.com/fwlink/?LinkID=124807" style="text-decoration: none;"&amp;gt;
	            &amp;lt;img src="http://go.microsoft.com/fwlink/?LinkId=108181" alt="Get Microsoft Silverlight" style="border-style: none"/&amp;gt;
            &amp;lt;/a&amp;gt;
        &amp;lt;/object&amp;gt;
        &amp;lt;iframe style='visibility:hidden;height:0;width:0;border:0px'&amp;gt;&amp;lt;/iframe&amp;gt;
    &amp;lt;/div&amp;gt;&lt;/pre&gt;
&lt;/div&gt;
&gt;
&lt;p&gt;
&lt;/p&gt;
&lt;p&gt;
&lt;strong&gt;&lt;a name="SecondaryDownload"&gt;Secondary Download&lt;/a&gt;&lt;/strong&gt;
&lt;/p&gt;
&lt;p&gt;
Once the application is loaded, the constructor of the main code behind Page class
is called
&lt;/p&gt;
&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:812469c5-0cb0-4c63-8c15-c81123a09de7:da725dfa-9c2c-4a24-b70a-275649cd0c34" class="wlWriterEditableSmartContent"&gt;&lt;pre name="code" class="c#"&gt;        public Page()
        {
            InitializeComponent();

            var controller = new Controller();
            controller.Launch();
            mainPage = controller.Windows[0];

            KeyUp += new KeyEventHandler(Page_KeyUp);

            LayoutRoot.Children.Add(mainPage);
        }&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;
Which calls starts a Controller class, which in turn starts the asynchronous requests
for the letters and the initial media file.
&lt;/p&gt;
&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:812469c5-0cb0-4c63-8c15-c81123a09de7:76737be7-54a5-4b40-ad54-5f6f0d8e4f3f" class="wlWriterEditableSmartContent"&gt;&lt;pre name="code" class="c#"&gt;       public void Launch()
       {
           CheckForUpdatesAsync();

           SetupInitialWindowState();

           //here to pre-cache letter
           CoolLetter.InitLetterStateAsync();

           //Startup sound
           audio.PlayWavResourceYield(".Resources.Sounds." + "EditedJackPlaysBabySmash.wav");
       }&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;
&lt;/p&gt;
&lt;p&gt;
&lt;/p&gt;
&lt;p&gt;
The rest of this section will discuss how the letters stored in the secondary BabySmashEnglishLetters.xap
are downloaded and stored for use when the user presses a key. The initial technique
for loading the audio files is discussed in the implementation of PlayWavResourceYeild
in the &lt;a href="#LoadOnDemand"&gt;Load On Demand&lt;/a&gt; section below.
&lt;/p&gt;
&lt;p&gt;
&lt;/p&gt;
&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:812469c5-0cb0-4c63-8c15-c81123a09de7:932969bf-421c-471a-8558-bd25b5be91e1" class="wlWriterEditableSmartContent"&gt;&lt;pre name="code" class="c#"&gt;   public partial class CoolLetter 
	{
      public CoolLetter()
      {
         this.InitializeComponent();
      }

	    static readonly Dictionary&amp;lt;string, string&amp;gt; _letters = InitLetters();

        public static void InitLetterStateAsync()
        {
            // No work to do as the private _letters will be initialised above
        }&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;
&lt;/p&gt;
&lt;p&gt;
The InitLetters method is called by the .Net Framework when the static class is initialised.
The method utilises a WebClient class instance to asynchronously download the required
secondary assembly 
&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:812469c5-0cb0-4c63-8c15-c81123a09de7:bff9f2e3-9797-4af9-ab96-5f74854e1d31" class="wlWriterEditableSmartContent"&gt;&lt;pre name="code" class="c#"&gt;       private static Dictionary&amp;lt;string, string&amp;gt; InitLetters()
       {
           var addressUri = new Uri("BabySmashEnglishLetters.xap", UriKind.Relative);
           var letterLoader = new WebClient();
           letterLoader.OpenReadCompleted += letterAssemblyLoaded;
           letterLoader.OpenReadAsync(addressUri);
           return new Dictionary&amp;lt;string, string&amp;gt;();
       }&lt;/pre&gt;
&lt;/div&gt;
&gt;
&lt;p&gt;
&lt;/p&gt;
&lt;p&gt;
Once the asynchronous call is completed, reflection is used to dynamically call the
GenerateAlphanumericCharacters method of the AlphaNumericLetterGenerator class. 
&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:812469c5-0cb0-4c63-8c15-c81123a09de7:9c10caac-a8aa-4b39-a4e7-bdcc75f94798" class="wlWriterEditableSmartContent"&gt;&lt;pre name="code" class="c#"&gt;	    private static void letterAssemblyLoaded(object sender, OpenReadCompletedEventArgs e)
	    {
	        if ((e.Error != null) || e.Cancelled) return;

	        // Convert the downloaded stream into an assembly
	        var a = LoadAssemblyFromXap("BabySmashLetters.dll", e.Result);

	        var generator = a.CreateInstance("BabySmashLetters.AlphaNumericLetterGenerator");

	        if (generator == null) return;


	        var generated = generator.GetType().InvokeMember("GenerateAlpanumericCharacters", BindingFlags.Public | BindingFlags.InvokeMethod | BindingFlags.Instance, null, generator,null) as Dictionary&amp;lt;string, string&amp;gt;;
            if ( generated == null ) return;
	        foreach (var pair in generated)
	        {
	            _letters.Add(pair.Key, pair.Value);
	        }
	    }
&lt;/pre&gt;
&lt;/div&gt;
Loading the assembly is achieved by extracting the requested assembly from the xap
stream as follows.&gt;
&lt;p&gt;
&lt;/p&gt;
&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:812469c5-0cb0-4c63-8c15-c81123a09de7:9ae34c5c-eaa7-4c91-8d41-7aabae51e5ec" class="wlWriterEditableSmartContent"&gt;&lt;pre name="code" class="c#"&gt;	    public static Assembly LoadAssemblyFromXap(string relativeUriString, Stream xapPackageStream)
        {
            var uri = new Uri(relativeUriString, UriKind.Relative);
            var xapPackageSri = new StreamResourceInfo(xapPackageStream, null);
            var assemblySri = Application.GetResourceStream(xapPackageSri, uri);

            var assemblyPart = new AssemblyPart();
            var a = assemblyPart.Load(assemblySri.Stream);
            return a;
        }&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;
&lt;strong&gt;&lt;a name="LoadOnDemand"&gt;Load On Demand&lt;/a&gt;&lt;/strong&gt;
&lt;/p&gt;
&lt;p&gt;
By the time that the third stage of load on demand media files is reached the main
application is loaded, the secondary assembly is loading or loaded and the introduction
sound file has played. This has resulted in the download of around 160kb of files
and kept the time wait time for the user to a minimum.
&lt;/p&gt;
&lt;p&gt;
Loading the external dependencies has also given us the following advantages:
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
Allow for the possibility of localising the application to other non-english languages. 
&lt;li&gt;
Allow media files to be updated independently of the compiled code. 
&lt;li&gt;
Allow the media files to to be sourced from alternative locations e.g. File stored
locally on the client computer. 
&lt;li&gt;
Reduced bandwidth from 406kb for users that visit the page but do not start using
the application.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
The process for loading the additional media files is similar to the process to load
the assembly secondary xap file but has the additional complexity that the user can
be pressing keys very quickly. As a result the application needs to respond very quickly
by queuing the key requests then asynchronously downloading and playing the associated
sound file.
&lt;/p&gt;
&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:812469c5-0cb0-4c63-8c15-c81123a09de7:92cbb09d-e85b-4e5e-a9b9-3acafda7b8f3" class="wlWriterEditableSmartContent"&gt;&lt;pre name="code" class="c#"&gt;        public void PlayWavResourceYield(string s)
        {
            if (player == null)
                return;

            if (inPlay &amp;amp;&amp;amp; soundsToPlay.Count &amp;lt; 5)
            {
                soundsToPlay.Enqueue(s);
                return;
            }

            PlaySound(s);
        }
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;
If the application is currently playing a media file then the request is placed on
queue for later playback otherwise the sound will be played. If the media file is
requested for the first time ten it will be asynchronously downloaded, added to the
available media list then played.
&lt;/p&gt;
&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:812469c5-0cb0-4c63-8c15-c81123a09de7:ebef9bbf-7910-446f-b440-e2ea6ebbb0d6" class="wlWriterEditableSmartContent"&gt;&lt;pre name="code" class="c#"&gt;        private void PlaySound(string resourceName)
        {
            var mediaFile = GetMediaFileFromResourceName(resourceName);

            lock (media)
            {
                if (media.ContainsKey(mediaFile) == false)
                {
                    var wc = new WebClient();
                    wc.OpenReadCompleted += delegate(object sender, OpenReadCompletedEventArgs e)
                                                {
                                                    lock (media)
                                                    {
                                                        if (media.ContainsKey(mediaFile) != false) return;
                                                        media.Add(mediaFile, e.Result);
                                                        player.SetSource(e.Result);
                                                    }
                                                };
                    wc.OpenReadAsync(new Uri(mediaFile, UriKind.Relative));
                    return;
                }
            }

            var sound = media[mediaFile];
            if (sound == null) return;
            if (sound.CanSeek) sound.Position = 0;
            player.SetSource(sound);
        }&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;
If the media file is requested again then the version available in the media list
will be reused and played to the user. 
&lt;/p&gt;
&lt;h2&gt;Summary
&lt;/h2&gt;
&lt;p&gt;
In summary when looking Silverlight applications one aim is to improve the perceived
user performance by:
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
Quick Feedback – Provide quick feedback to the user so that the can interact with
application. Small initial xap file allowed the “Smash Canvas” and initial introduction
message where played without the overhead of other resources. 
&lt;li&gt;
Thinking Asynchronously - Performing actions asynchronously so that the user action
is not blocked while necessary processing is occurring.&amp;nbsp; Requesting media on
demand allowed content to played as needed. 
&lt;li&gt;
Separating The Application – Look at architecting the application so that it can be
split into different parts that can be managed independently if required. Using separated
code/media files allows for further expansion points and hosting options.&lt;/li&gt;
&lt;/ul&gt;
&lt;img width="0" height="0" src="http://www.garchibald.com/blog/aggbug.ashx?id=cacc676a-6f06-41e3-a412-aa31dbe0e74e" /&gt;</description>
      <comments>http://www.garchibald.com/blog/CommentView,guid,cacc676a-6f06-41e3-a412-aa31dbe0e74e.aspx</comments>
      <category>babysmash</category>
      <category>silverlight</category>
    </item>
    <item>
      <trackback:ping>http://www.garchibald.com/blog/Trackback.aspx?guid=2d6f03d3-aab7-451f-ae76-1ee8abf3c5c3</trackback:ping>
      <pingback:server>http://www.garchibald.com/blog/pingback.aspx</pingback:server>
      <pingback:target>http://www.garchibald.com/blog/PermaLink,guid,2d6f03d3-aab7-451f-ae76-1ee8abf3c5c3.aspx</pingback:target>
      <dc:creator>Grant Archibald</dc:creator>
      <wfw:comment>http://www.garchibald.com/blog/CommentView,guid,2d6f03d3-aab7-451f-ae76-1ee8abf3c5c3.aspx</wfw:comment>
      <wfw:commentRss>http://www.garchibald.com/blog/SyndicationService.asmx/GetEntryCommentsRss?guid=2d6f03d3-aab7-451f-ae76-1ee8abf3c5c3</wfw:commentRss>
      <body xmlns="http://www.w3.org/1999/xhtml">
        <p>
In this post I will briefly discuss using Google Analytics in conjunction with Silverlight,
to track the version installed and your application usage patterns.
</p>
        <p>
Following on from Nikhil Kothari's blog entry on <a href="http://www.nikhilk.net/Silverlight-Analytics.aspx">Tracking
Silverlight-enabled Browsers via Analytics</a> I have made a few tweaks to the original
JavaScript that can be placed at the end of your HTML document after pageTracker has
been initialised to provide for the following.
</p>
        <ul>
          <li>
A change from the old <b>__utmSetVar</b> function in the urchin.js tracking to the
new <strong>pageTracker._setVar</strong> in ga.js version for the following <a href="http://www.roirevolution.com/blog/2008/01/should_you_join_the_migration_urchinjs_migrates_to.html">reasons</a>. 
</li>
          <li>
A check for Linux to distinguish between Moonlight usage versus Silverlight.</li>
        </ul>
        <p>
Based on this information it may help you further refine your Silverlight strategy
based on your visitors e.g.
</p>
        <ul>
          <li>
The split in visitor numbers between Silverlight Version 1 or 2. Important in terms
of:<br /><ul><li>
Install experience. 
</li><li>
Documentation on why the user should upgrade (Features/Differences). 
</li><li>
What messages should be displayed to the user of an older Version 1.0 client. 
</li><li>
Compatibility with the upcoming <a href="http://silverlight.net/learn/mobile.aspx">Silverlight
1.0 for Mobile</a> which will initially only support Version 1.</li></ul></li>
          <li>
Determine the cross platform use of your application. 
</li>
          <li>
The possible need to cater for font availability across different operating systems.</li>
        </ul>
        <p>
Also as indicated in the comments of Nikhil's blog post Jeff Wilcox has a great post <a href="http://blogs.msdn.com/jeffwilcox/archive/2007/10/01/using-google-analytics-with-rich-managed-web-applications-in-silverlight.aspx">Using
Google Analytics with rich (managed) web applications in Silverlight</a> that explores
how to integrate tracking within your Silverlight application.
</p>
        <!-- code formatted by http://manoli.net/csharpformat/ -->
        <div class="csharpcode">
          <pre class="alt">
            <span class="kwrd">&lt;</span>
            <span class="html">script</span>
            <span class="attr">type</span>
            <span class="kwrd">="text/javascript"</span>
            <span class="kwrd">&gt;</span>
          </pre>
          <pre> </pre>
          <pre class="alt">
            <span class="kwrd">function</span> getSilverlightVersion() {</pre>
          <pre> </pre>
          <pre class="alt">
            <span class="kwrd">var</span> version = <span class="str">''</span>;</pre>
          <pre> </pre>
          <pre class="alt">
            <span class="kwrd">var</span> container = <span class="kwrd">null</span>;</pre>
          <pre> </pre>
          <pre class="alt">
            <span class="kwrd">try</span> {</pre>
          <pre> </pre>
          <pre class="alt">
            <span class="kwrd">var</span> control = <span class="kwrd">null</span>;</pre>
          <pre> </pre>
          <pre class="alt">
            <span class="kwrd">var</span> product = <span class="str">'Silverlight'</span>;</pre>
          <pre> </pre>
          <pre class="alt">
            <span class="kwrd">if</span> ( navigator.userAgent.indexOf(<span class="str">"Linux"</span>)!=-1)
product=<span class="str">"Moonlight"</span>;</pre>
          <pre> </pre>
          <pre class="alt">
            <span class="kwrd">if</span> (window.ActiveXObject) {</pre>
          <pre> </pre>
          <pre class="alt">            control = <span class="kwrd">new</span> ActiveXObject(<span class="str">'AgControl.AgControl'</span>);</pre>
          <pre> </pre>
          <pre class="alt">        }</pre>
          <pre> </pre>
          <pre class="alt">
            <span class="kwrd">else</span> {</pre>
          <pre> </pre>
          <pre class="alt">
            <span class="kwrd">if</span> (navigator.plugins[<span class="str">'Silverlight
Plug-In'</span>]) {</pre>
          <pre> </pre>
          <pre class="alt">                container = document.createElement(<span class="str">'div'</span>);</pre>
          <pre> </pre>
          <pre class="alt">                document.body.appendChild(container);</pre>
          <pre> </pre>
          <pre class="alt">                container.innerHTML= <span class="str">'&lt;embed
type="application/x-silverlight" src="data:," /&gt;'</span>;</pre>
          <pre> </pre>
          <pre class="alt">                control = container.childNodes[0];</pre>
          <pre> </pre>
          <pre class="alt">            }</pre>
          <pre> </pre>
          <pre class="alt">        }</pre>
          <pre> </pre>
          <pre class="alt">
            <span class="kwrd">if</span> (control) {</pre>
          <pre> </pre>
          <pre class="alt">
            <span class="kwrd">if</span> (control.isVersionSupported(<span class="str">'2.0'</span>))
{ version = product + <span class="str">'/2.0'</span>; }</pre>
          <pre> </pre>
          <pre class="alt">
            <span class="kwrd">else</span>
            <span class="kwrd">if</span> (control.isVersionSupported(<span class="str">'1.0'</span>))
{ version = product + <span class="str">'/1.0'</span>; }</pre>
          <pre> </pre>
          <pre class="alt">        }</pre>
          <pre> </pre>
          <pre class="alt">    }</pre>
          <pre> </pre>
          <pre class="alt">
            <span class="kwrd">catch</span> (e) { }</pre>
          <pre> </pre>
          <pre class="alt">
            <span class="kwrd">if</span> (container) {</pre>
          <pre> </pre>
          <pre class="alt">        document.body.removeChild(container);</pre>
          <pre> </pre>
          <pre class="alt">    }</pre>
          <pre> </pre>
          <pre class="alt">
            <span class="kwrd">return</span> version;</pre>
          <pre> </pre>
          <pre class="alt">}</pre>
          <pre> </pre>
          <pre class="alt">
            <span class="kwrd">if</span> ( pageTracker )</pre>
          <pre> </pre>
          <pre class="alt">    pageTracker._setVar(getSilverlightVersion()); </pre>
          <pre> </pre>
          <pre class="alt">
            <span class="kwrd">&lt;/</span>
            <span class="html">script</span>
            <span class="kwrd">&gt;</span>
          </pre>
        </div>
        <img width="0" height="0" src="http://www.garchibald.com/blog/aggbug.ashx?id=2d6f03d3-aab7-451f-ae76-1ee8abf3c5c3" />
      </body>
      <title>Tracking Silverlight And Moonlight Enabled Browsers via Google Analytics</title>
      <guid isPermaLink="false">http://www.garchibald.com/blog/PermaLink,guid,2d6f03d3-aab7-451f-ae76-1ee8abf3c5c3.aspx</guid>
      <link>http://www.garchibald.com/blog/2008/08/10/TrackingSilverlightAndMoonlightEnabledBrowsersViaGoogleAnalytics.aspx</link>
      <pubDate>Sun, 10 Aug 2008 10:16:20 GMT</pubDate>
      <description>&lt;p&gt;
In this post I will briefly discuss using Google Analytics in conjunction with Silverlight,
to track the version installed and your application usage patterns.
&lt;/p&gt;
&lt;p&gt;
Following on from Nikhil Kothari's blog entry on &lt;a href="http://www.nikhilk.net/Silverlight-Analytics.aspx"&gt;Tracking
Silverlight-enabled Browsers via Analytics&lt;/a&gt; I have made a few tweaks to the original
JavaScript that can be placed at the end of your HTML document after pageTracker has
been initialised to provide for the following.
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
A change from the old &lt;b&gt;__utmSetVar&lt;/b&gt; function in the urchin.js tracking to the
new &lt;strong&gt;pageTracker._setVar&lt;/strong&gt; in ga.js version for the following &lt;a href="http://www.roirevolution.com/blog/2008/01/should_you_join_the_migration_urchinjs_migrates_to.html"&gt;reasons&lt;/a&gt;. 
&lt;/li&gt;
&lt;li&gt;
A check for Linux to distinguish between Moonlight usage versus Silverlight.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
Based on this information it may help you further refine your Silverlight strategy
based on your visitors e.g.
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
The split in visitor numbers between Silverlight Version 1 or 2. Important in terms
of:&lt;br&gt;
&lt;ul&gt;
&lt;li&gt;
Install experience. 
&lt;/li&gt;
&lt;li&gt;
Documentation on why the user should upgrade (Features/Differences). 
&lt;/li&gt;
&lt;li&gt;
What messages should be displayed to the user of an older Version 1.0 client. 
&lt;/li&gt;
&lt;li&gt;
Compatibility with the upcoming &lt;a href="http://silverlight.net/learn/mobile.aspx"&gt;Silverlight
1.0 for Mobile&lt;/a&gt; which will initially only support Version 1.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
Determine the cross platform use of your application. 
&lt;/li&gt;
&lt;li&gt;
The possible need to cater for font availability across different operating systems.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
Also as indicated in the comments of Nikhil's blog post Jeff Wilcox has a great post &lt;a href="http://blogs.msdn.com/jeffwilcox/archive/2007/10/01/using-google-analytics-with-rich-managed-web-applications-in-silverlight.aspx"&gt;Using
Google Analytics with rich (managed) web applications in Silverlight&lt;/a&gt; that explores
how to integrate tracking within your Silverlight application.
&lt;/p&gt;
&lt;!-- code formatted by http://manoli.net/csharpformat/ --&gt;
&lt;div class="csharpcode"&gt;
&lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;script&lt;/span&gt; &lt;span class="attr"&gt;type&lt;/span&gt;&lt;span class="kwrd"&gt;="text/javascript"&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;
&lt;pre&gt;&amp;nbsp;&lt;/pre&gt;
&lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;function&lt;/span&gt; getSilverlightVersion() {&lt;/pre&gt;
&lt;pre&gt;&amp;nbsp;&lt;/pre&gt;
&lt;pre class="alt"&gt;    &lt;span class="kwrd"&gt;var&lt;/span&gt; version = &lt;span class="str"&gt;''&lt;/span&gt;;&lt;/pre&gt;
&lt;pre&gt;&amp;nbsp;&lt;/pre&gt;
&lt;pre class="alt"&gt;    &lt;span class="kwrd"&gt;var&lt;/span&gt; container = &lt;span class="kwrd"&gt;null&lt;/span&gt;;&lt;/pre&gt;
&lt;pre&gt;&amp;nbsp;&lt;/pre&gt;
&lt;pre class="alt"&gt;    &lt;span class="kwrd"&gt;try&lt;/span&gt; {&lt;/pre&gt;
&lt;pre&gt;&amp;nbsp;&lt;/pre&gt;
&lt;pre class="alt"&gt;        &lt;span class="kwrd"&gt;var&lt;/span&gt; control = &lt;span class="kwrd"&gt;null&lt;/span&gt;;&lt;/pre&gt;
&lt;pre&gt;&amp;nbsp;&lt;/pre&gt;
&lt;pre class="alt"&gt;        &lt;span class="kwrd"&gt;var&lt;/span&gt; product = &lt;span class="str"&gt;'Silverlight'&lt;/span&gt;;&lt;/pre&gt;
&lt;pre&gt;&amp;nbsp;&lt;/pre&gt;
&lt;pre class="alt"&gt;        &lt;span class="kwrd"&gt;if&lt;/span&gt; ( navigator.userAgent.indexOf(&lt;span class="str"&gt;"Linux"&lt;/span&gt;)!=-1)
product=&lt;span class="str"&gt;"Moonlight"&lt;/span&gt;;&lt;/pre&gt;
&lt;pre&gt;&amp;nbsp;&lt;/pre&gt;
&lt;pre class="alt"&gt;        &lt;span class="kwrd"&gt;if&lt;/span&gt; (window.ActiveXObject) {&lt;/pre&gt;
&lt;pre&gt;&amp;nbsp;&lt;/pre&gt;
&lt;pre class="alt"&gt;            control = &lt;span class="kwrd"&gt;new&lt;/span&gt; ActiveXObject(&lt;span class="str"&gt;'AgControl.AgControl'&lt;/span&gt;);&lt;/pre&gt;
&lt;pre&gt;&amp;nbsp;&lt;/pre&gt;
&lt;pre class="alt"&gt;        }&lt;/pre&gt;
&lt;pre&gt;&amp;nbsp;&lt;/pre&gt;
&lt;pre class="alt"&gt;        &lt;span class="kwrd"&gt;else&lt;/span&gt; {&lt;/pre&gt;
&lt;pre&gt;&amp;nbsp;&lt;/pre&gt;
&lt;pre class="alt"&gt;            &lt;span class="kwrd"&gt;if&lt;/span&gt; (navigator.plugins[&lt;span class="str"&gt;'Silverlight
Plug-In'&lt;/span&gt;]) {&lt;/pre&gt;
&lt;pre&gt;&amp;nbsp;&lt;/pre&gt;
&lt;pre class="alt"&gt;                container = document.createElement(&lt;span class="str"&gt;'div'&lt;/span&gt;);&lt;/pre&gt;
&lt;pre&gt;&amp;nbsp;&lt;/pre&gt;
&lt;pre class="alt"&gt;                document.body.appendChild(container);&lt;/pre&gt;
&lt;pre&gt;&amp;nbsp;&lt;/pre&gt;
&lt;pre class="alt"&gt;                container.innerHTML= &lt;span class="str"&gt;'&amp;lt;embed
type="application/x-silverlight" src="data:," /&amp;gt;'&lt;/span&gt;;&lt;/pre&gt;
&lt;pre&gt;&amp;nbsp;&lt;/pre&gt;
&lt;pre class="alt"&gt;                control = container.childNodes[0];&lt;/pre&gt;
&lt;pre&gt;&amp;nbsp;&lt;/pre&gt;
&lt;pre class="alt"&gt;            }&lt;/pre&gt;
&lt;pre&gt;&amp;nbsp;&lt;/pre&gt;
&lt;pre class="alt"&gt;        }&lt;/pre&gt;
&lt;pre&gt;&amp;nbsp;&lt;/pre&gt;
&lt;pre class="alt"&gt;        &lt;span class="kwrd"&gt;if&lt;/span&gt; (control) {&lt;/pre&gt;
&lt;pre&gt;&amp;nbsp;&lt;/pre&gt;
&lt;pre class="alt"&gt;            &lt;span class="kwrd"&gt;if&lt;/span&gt; (control.isVersionSupported(&lt;span class="str"&gt;'2.0'&lt;/span&gt;))
{ version = product + &lt;span class="str"&gt;'/2.0'&lt;/span&gt;; }&lt;/pre&gt;
&lt;pre&gt;&amp;nbsp;&lt;/pre&gt;
&lt;pre class="alt"&gt;            &lt;span class="kwrd"&gt;else&lt;/span&gt; &lt;span class="kwrd"&gt;if&lt;/span&gt; (control.isVersionSupported(&lt;span class="str"&gt;'1.0'&lt;/span&gt;))
{ version = product + &lt;span class="str"&gt;'/1.0'&lt;/span&gt;; }&lt;/pre&gt;
&lt;pre&gt;&amp;nbsp;&lt;/pre&gt;
&lt;pre class="alt"&gt;        }&lt;/pre&gt;
&lt;pre&gt;&amp;nbsp;&lt;/pre&gt;
&lt;pre class="alt"&gt;    }&lt;/pre&gt;
&lt;pre&gt;&amp;nbsp;&lt;/pre&gt;
&lt;pre class="alt"&gt;    &lt;span class="kwrd"&gt;catch&lt;/span&gt; (e) { }&lt;/pre&gt;
&lt;pre&gt;&amp;nbsp;&lt;/pre&gt;
&lt;pre class="alt"&gt;    &lt;span class="kwrd"&gt;if&lt;/span&gt; (container) {&lt;/pre&gt;
&lt;pre&gt;&amp;nbsp;&lt;/pre&gt;
&lt;pre class="alt"&gt;        document.body.removeChild(container);&lt;/pre&gt;
&lt;pre&gt;&amp;nbsp;&lt;/pre&gt;
&lt;pre class="alt"&gt;    }&lt;/pre&gt;
&lt;pre&gt;&amp;nbsp;&lt;/pre&gt;
&lt;pre class="alt"&gt;    &lt;span class="kwrd"&gt;return&lt;/span&gt; version;&lt;/pre&gt;
&lt;pre&gt;&amp;nbsp;&lt;/pre&gt;
&lt;pre class="alt"&gt;}&lt;/pre&gt;
&lt;pre&gt;&amp;nbsp;&lt;/pre&gt;
&lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;if&lt;/span&gt; ( pageTracker )&lt;/pre&gt;
&lt;pre&gt;&amp;nbsp;&lt;/pre&gt;
&lt;pre class="alt"&gt;    pageTracker._setVar(getSilverlightVersion()); &lt;/pre&gt;
&lt;pre&gt;&amp;nbsp;&lt;/pre&gt;
&lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;script&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;img width="0" height="0" src="http://www.garchibald.com/blog/aggbug.ashx?id=2d6f03d3-aab7-451f-ae76-1ee8abf3c5c3" /&gt;</description>
      <comments>http://www.garchibald.com/blog/CommentView,guid,2d6f03d3-aab7-451f-ae76-1ee8abf3c5c3.aspx</comments>
      <category>moonlight</category>
      <category>silverlight</category>
    </item>
    <item>
      <trackback:ping>http://www.garchibald.com/blog/Trackback.aspx?guid=ba69bc68-9ccd-4e9f-8b1b-b94b10b992c9</trackback:ping>
      <pingback:server>http://www.garchibald.com/blog/pingback.aspx</pingback:server>
      <pingback:target>http://www.garchibald.com/blog/PermaLink,guid,ba69bc68-9ccd-4e9f-8b1b-b94b10b992c9.aspx</pingback:target>
      <dc:creator>Grant Archibald</dc:creator>
      <wfw:comment>http://www.garchibald.com/blog/CommentView,guid,ba69bc68-9ccd-4e9f-8b1b-b94b10b992c9.aspx</wfw:comment>
      <wfw:commentRss>http://www.garchibald.com/blog/SyndicationService.asmx/GetEntryCommentsRss?guid=ba69bc68-9ccd-4e9f-8b1b-b94b10b992c9</wfw:commentRss>
      <body xmlns="http://www.w3.org/1999/xhtml">
        <p>
One of the original problems with the <a href="http://www.garchibald.com/Blog/2008/08/02/BabySmashOnSilverlight.aspx">initial
port of BabySmash to Silverlight</a> was loading the XAML storyboard animations for
UserControls. 
</p>
        <p>
In this post I will cover the changes required from a Silverlight point of view to
replace the RoutedEvent in the UserControl.Triggers section with either equivalent
XAML or C# code depending on your need. 
<br /></p>
        <h3>Refactoring Routed Event Changes
</h3>
        <p>
XAML in 
<abbr title="Windows Presentation Foundation">
WPF
</abbr>
allows tools such as Expression Blend to define triggered events without the use of
code. In the original BabySmash XAML similar the following exists, which starts a
storyboard when the user control is loaded e.g.
</p>
        <p>
 
</p>
        <!-- code formatted by http://manoli.net/csharpformat/ -->
        <div class="csharpcode">
          <pre class="alt">
            <span class="kwrd">&lt;</span>
            <span class="html">UserControl.Triggers</span>
            <span class="kwrd">&gt;</span>
          </pre>
          <pre>
            <span class="kwrd">&lt;</span>
            <span class="html">EventTrigger</span>
            <span class="attr">RoutedEvent</span>
            <span class="kwrd">="FrameworkElement.Loaded"</span>
            <span class="kwrd">&gt;</span>
          </pre>
          <pre class="alt">
            <span class="kwrd">&lt;</span>
            <span class="html">BeginStoryboard</span>
            <span class="attr">Storyboard</span>
            <span class="kwrd">="{StaticResource
EyesSB}"</span>
            <span class="kwrd">/&gt;</span>
          </pre>
          <pre>
            <span class="kwrd">&lt;/</span>
            <span class="html">EventTrigger</span>
            <span class="kwrd">&gt;</span>
          </pre>
          <pre class="alt">
            <span class="kwrd">&lt;/</span>
            <span class="html">UserControl.Triggers</span>
            <span class="kwrd">&gt;</span>
          </pre>
        </div>
        <p>
 
</p>
        <p>
As <a href="http://silverlight.net/forums/t/12378.aspx">Shawn Wildermuth indicated
on the Silverlight forums</a> Routed Events are not available in Silverlight 2.0 Beta
2 as a result the event trigger either needs to be deleted or commented out in the
XAML and alternative approach taken to achieve the same result. 
</p>
        <p>
Looking Richard Griffen's <a href="http://blogs.conchango.com/richardgriffin/archive/2008/07/23/silverlight-tweening-adventures-with-baby-smash.aspx">Silverlight
Tweening Adventures with Baby Smash!</a> MyTweenerTest it shows that the XAML can
easily be refactored by moving the animations inside a BeginStoryBoard element. Hence
not require the use of the Loaded event e.g.
</p>
        <p>
 
</p>
        <!-- code formatted by http://manoli.net/csharpformat/ -->
        <div class="csharpcode">
          <pre class="alt">
            <span class="kwrd">&lt;</span>
            <span class="html">UserControl.Resources</span>
            <span class="kwrd">&gt;</span>
          </pre>
          <pre>
            <span class="kwrd">&lt;</span>
            <span class="html">BeginStoryboard</span>
            <span class="attr">x:Name</span>
            <span class="kwrd">="Begin"</span>
            <span class="kwrd">&gt;</span>
          </pre>
          <pre class="alt">
            <span class="kwrd">&lt;</span>
            <span class="html">Storyboard</span>
            <span class="attr">x:Name</span>
            <span class="kwrd">="EyesSB"</span>
            <span class="attr">RepeatBehavior</span>
            <span class="kwrd">="Forever"</span>
            <span class="kwrd">&gt;</span>
          </pre>
          <pre>
            <span class="kwrd">&lt;</span>
            <span class="html">DoubleAnimationUsingKeyFrames</span>
            <span class="attr">BeginTime</span>
            <span class="kwrd">="00:00:00"</span>
            <span class="attr">Storyboard</span>.<span class="attr">TargetName</span><span class="kwrd">="CircleEye1"</span><span class="attr">Storyboard</span>.<span class="attr">TargetProperty</span><span class="kwrd">="(UIElement.Opacity)"</span><span class="attr">RepeatBehavior</span><span class="kwrd">="Forever"</span><span class="kwrd">&gt;</span></pre>
          <pre class="alt">
            <span class="kwrd">&lt;</span>
            <span class="html">SplineDoubleKeyFrame</span>
            <span class="attr">KeyTime</span>
            <span class="kwrd">="00:00:02.1000000"</span>
            <span class="attr">Value</span>
            <span class="kwrd">="1"</span>
            <span class="kwrd">/&gt;</span>
          </pre>
          <pre>
            <span class="kwrd">&lt;</span>
            <span class="html">SplineDoubleKeyFrame</span>
            <span class="attr">KeyTime</span>
            <span class="kwrd">="00:00:02.1000000"</span>
            <span class="attr">Value</span>
            <span class="kwrd">="0"</span>
            <span class="kwrd">/&gt;</span>
          </pre>
          <pre class="alt">
            <span class="kwrd">&lt;</span>
            <span class="html">SplineDoubleKeyFrame</span>
            <span class="attr">KeyTime</span>
            <span class="kwrd">="00:00:02.300000"</span>
            <span class="attr">Value</span>
            <span class="kwrd">="0"</span>
            <span class="kwrd">/&gt;</span>
          </pre>
          <pre>
            <span class="kwrd">&lt;</span>
            <span class="html">SplineDoubleKeyFrame</span>
            <span class="attr">KeyTime</span>
            <span class="kwrd">="00:00:02.300000"</span>
            <span class="attr">Value</span>
            <span class="kwrd">="1"</span>
            <span class="kwrd">/&gt;</span>
          </pre>
          <pre class="alt">
            <span class="kwrd">&lt;</span>
            <span class="html">SplineDoubleKeyFrame</span>
            <span class="attr">KeyTime</span>
            <span class="kwrd">="00:00:7.300000"</span>
            <span class="attr">Value</span>
            <span class="kwrd">="1"</span>
            <span class="kwrd">/&gt;</span>
          </pre>
          <pre>
            <span class="kwrd">&lt;/</span>
            <span class="html">DoubleAnimationUsingKeyFrames</span>
            <span class="kwrd">&gt;</span>
          </pre>
          <pre class="alt">
            <span class="kwrd">&lt;</span>
            <span class="html">DoubleAnimationUsingKeyFrames</span>
            <span class="attr">BeginTime</span>
            <span class="kwrd">="00:00:00"</span>
            <span class="attr">Storyboard</span>.<span class="attr">TargetName</span><span class="kwrd">="CircleEye2"</span><span class="attr">Storyboard</span>.<span class="attr">TargetProperty</span><span class="kwrd">="(UIElement.Opacity)"</span><span class="attr">RepeatBehavior</span><span class="kwrd">="Forever"</span><span class="kwrd">&gt;</span></pre>
          <pre>
            <span class="kwrd">&lt;</span>
            <span class="html">SplineDoubleKeyFrame</span>
            <span class="attr">KeyTime</span>
            <span class="kwrd">="00:00:02.1000000"</span>
            <span class="attr">Value</span>
            <span class="kwrd">="1"</span>
            <span class="kwrd">/&gt;</span>
          </pre>
          <pre class="alt">
            <span class="kwrd">&lt;</span>
            <span class="html">SplineDoubleKeyFrame</span>
            <span class="attr">KeyTime</span>
            <span class="kwrd">="00:00:02.1000000"</span>
            <span class="attr">Value</span>
            <span class="kwrd">="0"</span>
            <span class="kwrd">/&gt;</span>
          </pre>
          <pre>
            <span class="kwrd">&lt;</span>
            <span class="html">SplineDoubleKeyFrame</span>
            <span class="attr">KeyTime</span>
            <span class="kwrd">="00:00:02.300000"</span>
            <span class="attr">Value</span>
            <span class="kwrd">="0"</span>
            <span class="kwrd">/&gt;</span>
          </pre>
          <pre class="alt">
            <span class="kwrd">&lt;</span>
            <span class="html">SplineDoubleKeyFrame</span>
            <span class="attr">KeyTime</span>
            <span class="kwrd">="00:00:02.300000"</span>
            <span class="attr">Value</span>
            <span class="kwrd">="1"</span>
            <span class="kwrd">/&gt;</span>
          </pre>
          <pre>
            <span class="kwrd">&lt;</span>
            <span class="html">SplineDoubleKeyFrame</span>
            <span class="attr">KeyTime</span>
            <span class="kwrd">="00:00:7.300000"</span>
            <span class="attr">Value</span>
            <span class="kwrd">="1"</span>
            <span class="kwrd">/&gt;</span>
          </pre>
          <pre class="alt">
            <span class="kwrd">&lt;/</span>
            <span class="html">DoubleAnimationUsingKeyFrames</span>
            <span class="kwrd">&gt;</span>
          </pre>
          <pre>
            <span class="kwrd">&lt;/</span>
            <span class="html">Storyboard</span>
            <span class="kwrd">&gt;</span>
          </pre>
          <pre class="alt">
            <span class="kwrd">&lt;/</span>
            <span class="html">BeginStoryboard</span>
            <span class="kwrd">&gt;</span>
          </pre>
          <pre>
            <span class="kwrd">&lt;/</span>
            <span class="html">UserControl.Resources</span>
            <span class="kwrd">&gt;</span>
          </pre>
        </div>
        <p>
 
</p>
        <p>
However I wanted to look at a way of using the code to apply dynamic changes to the
XAML and walkthough the process of refactoring.
</p>
        <p>
 
</p>
        <h3>Routed Event  - Replacement Using Code
</h3>
        <p>
 
</p>
        <p>
In the next few steps I will introduce a series of refactorings that create the code
behind and them reduce the amount of code that needs to be repeated.
</p>
        <ol>
          <li>
The first refactoring defines a Loaded event in each of the shapes (e.g. Circle, Heart,
Hexagon, Rectangle, Square, Star, Trapezoid and Triangle) 
</li>
          <li>
The next refactoring reduces the code by using a extension method to remove the need
for the duplicated event handler implementation. 
</li>
          <li>
The third refactoring introduces a convention that the eyes animation storyboard should
be called EyesSB and applied to UserControls.</li>
        </ol>
        <p>
Overall the aim of the refactorings are to:
</p>
        <ol>
          <li>
Reduce the amount of code that has to be implemented by developers or designers. 
</li>
          <li>
Provides documented implementation of how the storyboard is applied. 
</li>
          <li>
Move the code required into a separate class that can be unit tested. 
</li>
          <li>
Allows new shapes to be created that do not require any additional code to be written.</li>
        </ol>
        <p>
Overall the effect is that the Routed event section is no longer requires in each
of the XAML files. 
</p>
        <p>
 
</p>
        <h4>Refactoring 1 - Inline Event
</h4>
        <p>
 
</p>
        <p>
Change the code behind for each of the shapes and add a ShapedLoaded event e.g.
</p>
        <p>
 
</p>
        <!-- code formatted by http://manoli.net/csharpformat/ -->
        <div class="csharpcode">
          <pre class="alt">
            <span class="kwrd">public</span> CoolHeart()</pre>
          <pre>{</pre>
          <pre class="alt">
            <span class="kwrd">this</span>.InitializeComponent();</pre>
          <pre>
            <span class="kwrd">this</span>.Loaded
+= ShapeLoaded;</pre>
          <pre class="alt">}</pre>
          <pre> </pre>
          <pre class="alt">
            <span class="kwrd">private</span>
            <span class="kwrd">void</span> ShapeLoaded(<span class="kwrd">object</span> sender,
RoutedEventArgs e)</pre>
          <pre>{</pre>
          <pre class="alt">  var eyes = FindName(<span class="str">"EyesSB"</span>) <span class="kwrd">as</span> Storyboard;</pre>
          <pre> </pre>
          <pre class="alt">
            <span class="kwrd">if</span> (eyes
!= <span class="kwrd">null</span>)</pre>
          <pre>      eyes.Begin();</pre>
          <pre class="alt">}</pre>
        </div>
        <p>
 
</p>
        <h4>Refactoring 2 - Extension Method
</h4>
        <p>
 
</p>
        <p>
To aid with readability and reuse across all the shapes the refactoring above can
further be improved. By moving to an extension method in a separate class. As a result
we can remove ShapeLoaded from each shape e.g.
</p>
        <p>
 
</p>
        <!-- code formatted by http://manoli.net/csharpformat/ -->
        <div class="csharpcode">
          <pre class="alt">
            <span class="kwrd">public</span> CoolHeart()</pre>
          <pre>{</pre>
          <pre class="alt">
            <span class="kwrd">this</span>.InitializeComponent();</pre>
          <pre>
            <span class="kwrd">this</span>.Loaded
+= <span class="kwrd">this</span>.BeginStoryBoardAnimation(<span class="str">"EyesSB"</span>);</pre>
          <pre class="alt">}</pre>
        </div>
        <p>
 
</p>
        <p>
With the definition of the extension method as follows
</p>
        <p>
 
</p>
        <!-- code formatted by http://manoli.net/csharpformat/ -->
        <div class="csharpcode">
          <pre class="alt">
            <span class="rem">/// &lt;summary&gt;</span>
          </pre>
          <pre>
            <span class="rem">///
Helper class for XAML that performs common tasks</span>
          </pre>
          <pre class="alt">
            <span class="rem">///
&lt;/summary&gt;</span>
          </pre>
          <pre>
            <span class="kwrd">public</span>
            <span class="kwrd">static</span>
            <span class="kwrd">class</span> XamlHelper</pre>
          <pre class="alt">{</pre>
          <pre> </pre>
          <pre class="alt">
            <span class="rem">///
&lt;summary&gt;</span>
          </pre>
          <pre>
            <span class="rem">/// Begins the named storyboard
animation.</span>
          </pre>
          <pre class="alt">
            <span class="rem">/// &lt;/summary&gt;</span>
          </pre>
          <pre>
            <span class="rem">///
&lt;remarks&gt;No exception if thrown if the storyboard is not found&lt;/remarks&gt;</span>
          </pre>
          <pre class="alt">
            <span class="rem">///
&lt;param name="control"&gt;The control that contains the storyboard&lt;/param&gt;</span>
          </pre>
          <pre>
            <span class="rem">///
&lt;param name="storyboardName"&gt;Name of the storyboard to be started&lt;/param&gt;</span>
          </pre>
          <pre class="alt">
            <span class="rem">///
&lt;returns&gt;An event handler instance &lt;/returns&gt;</span>
          </pre>
          <pre>
            <span class="kwrd">public</span>
            <span class="kwrd">static</span> RoutedEventHandler
BeginStoryBoardAnimation(<span class="kwrd">this</span> UserControl control, <span class="kwrd">string</span> storyboardName)</pre>
          <pre class="alt">    {</pre>
          <pre>
            <span class="kwrd">return</span> (source,
args) =&gt;</pre>
          <pre class="alt">                   {</pre>
          <pre>                       var eyes = control.FindName(storyboardName) <span class="kwrd">as</span> Storyboard;</pre>
          <pre class="alt"> </pre>
          <pre>
            <span class="kwrd">if</span> (eyes
!= <span class="kwrd">null</span>)</pre>
          <pre class="alt">                           eyes.Begin();</pre>
          <pre>                   };</pre>
          <pre class="alt">    }</pre>
          <pre>}</pre>
        </div>
        <p>
 
</p>
        <h4>Refactoring 3 - Naming Convention
</h4>
        <p>
 
</p>
        <p>
Remove the initialisation of the Loaded event from the constructor of each shape and
update the FigureGenerator so that the EyesSB is started if it exists.
</p>
        <p>
 
</p>
        <!-- code formatted by http://manoli.net/csharpformat/ -->
        <div class="csharpcode">
          <pre class="alt">
            <span class="kwrd">public</span>
            <span class="kwrd">static</span> UserControl
NewUserControlFrom(FigureTemplate template)</pre>
          <pre>{</pre>
          <pre class="alt"> </pre>
          <pre>    UserControl retVal = <span class="kwrd">null</span>;</pre>
          <pre class="alt"> </pre>
          <pre>
            <span class="rem">//We'll
wait for Hardware Accelerated Shader Effects in SP1</span>
          </pre>
          <pre class="alt">
            <span class="kwrd">if</span> (template.Letter.Length
== 1 &amp;&amp; Char.IsLetterOrDigit(template.Letter[0]))</pre>
          <pre>    {</pre>
          <pre class="alt">          retVal = <span class="kwrd">new</span> CoolLetter(template.Fill,
template.Letter);</pre>
          <pre>    }</pre>
          <pre class="alt">
            <span class="kwrd">else</span>
          </pre>
          <pre>    {</pre>
          <pre class="alt">          retVal = template.GeneratorFunc(template.Fill);</pre>
          <pre>          retVal.StartStoryboardAnimation(<span class="str">"EyesSB"</span>);</pre>
          <pre class="alt">    }</pre>
        </div>
        <p>
 
</p>
        <p>
With the updated definition of the extension method
</p>
        <p>
 
</p>
        <!-- code formatted by http://manoli.net/csharpformat/ -->
        <div class="csharpcode">
          <pre class="alt">
            <span class="rem">/// &lt;summary&gt;</span>
          </pre>
          <pre> </pre>
          <pre class="alt">
            <span class="rem">/// Helper class for XAML that performs common
tasks</span>
          </pre>
          <pre> </pre>
          <pre class="alt">
            <span class="rem">/// &lt;/summary&gt;</span>
          </pre>
          <pre> </pre>
          <pre class="alt">
            <span class="kwrd">public</span>
            <span class="kwrd">static</span>
            <span class="kwrd">class</span> XamlHelper</pre>
          <pre> </pre>
          <pre class="alt">{</pre>
          <pre> </pre>
          <pre class="alt">
          </pre>
          <pre> </pre>
          <pre class="alt">
            <span class="rem">/// &lt;summary&gt;</span>
          </pre>
          <pre> </pre>
          <pre class="alt">
            <span class="rem">/// Begins the named story board animation.</span>
          </pre>
          <pre> </pre>
          <pre class="alt">
            <span class="rem">/// &lt;/summary&gt;</span>
          </pre>
          <pre> </pre>
          <pre class="alt">
            <span class="rem">/// &lt;remarks&gt;No exception if thrown if the
story board is not found&lt;/remarks&gt;</span>
          </pre>
          <pre> </pre>
          <pre class="alt">
            <span class="rem">/// &lt;param name="control"&gt;The control that
contains the story board&lt;/param&gt;</span>
          </pre>
          <pre> </pre>
          <pre class="alt">
            <span class="rem">/// &lt;param name="storyboardName"&gt;Name of
the storyboard to be started&lt;/param&gt;</span>
          </pre>
          <pre> </pre>
          <pre class="alt">
            <span class="rem">/// &lt;returns&gt;An event handler instance &lt;/returns&gt;</span>
          </pre>
          <pre> </pre>
          <pre class="alt">
            <span class="kwrd">public</span>
            <span class="kwrd">static</span> RoutedEventHandler
BeginStoryBoardAnimation(<span class="kwrd">this</span> UserControl control, <span class="kwrd">string</span> storyboardName)</pre>
          <pre> </pre>
          <pre class="alt">{</pre>
          <pre> </pre>
          <pre class="alt">
            <span class="kwrd">return</span> (source, args) =&gt; StartStoryboardAnimation(control,
storyboardName);</pre>
          <pre> </pre>
          <pre class="alt">}</pre>
          <pre> </pre>
          <pre class="alt">
          </pre>
          <pre> </pre>
          <pre class="alt">
            <span class="rem">/// &lt;summary&gt;</span>
          </pre>
          <pre> </pre>
          <pre class="alt">
            <span class="rem">/// Starts the named story board animation.</span>
          </pre>
          <pre> </pre>
          <pre class="alt">
            <span class="rem">/// &lt;/summary&gt;</span>
          </pre>
          <pre> </pre>
          <pre class="alt">
            <span class="rem">/// &lt;remarks&gt;No exception if thrown if the
story board is not found&lt;/remarks&gt;</span>
          </pre>
          <pre> </pre>
          <pre class="alt">
            <span class="rem">/// &lt;param name="control"&gt;The control that
contains the story board&lt;/param&gt;</span>
          </pre>
          <pre> </pre>
          <pre class="alt">
            <span class="rem">/// &lt;param name="storyboardName"&gt;Name of
the storyboard to be started&lt;/param&gt;</span>
          </pre>
          <pre> </pre>
          <pre class="alt">
            <span class="kwrd">public</span>
            <span class="kwrd">static</span>
            <span class="kwrd">void</span> StartStoryboardAnimation(<span class="kwrd">this</span> UserControl
control, <span class="kwrd">string</span> storyboardName)</pre>
          <pre> </pre>
          <pre class="alt">{</pre>
          <pre> </pre>
          <pre class="alt">    var eyes = control.FindName(storyboardName) <span class="kwrd">as</span> Storyboard;</pre>
          <pre> </pre>
          <pre class="alt">
          </pre>
          <pre> </pre>
          <pre class="alt">
            <span class="kwrd">if</span> (eyes != <span class="kwrd">null</span>)</pre>
          <pre> </pre>
          <pre class="alt">        eyes.Begin();</pre>
          <pre> </pre>
          <pre class="alt">}</pre>
        </div>
        <br />
        <img width="0" height="0" src="http://www.garchibald.com/blog/aggbug.ashx?id=ba69bc68-9ccd-4e9f-8b1b-b94b10b992c9" />
      </body>
      <title>BabySmash Silverlight Refactorings - Part 2 - Routed Events</title>
      <guid isPermaLink="false">http://www.garchibald.com/blog/PermaLink,guid,ba69bc68-9ccd-4e9f-8b1b-b94b10b992c9.aspx</guid>
      <link>http://www.garchibald.com/blog/2008/08/08/BabySmashSilverlightRefactoringsPart2RoutedEvents.aspx</link>
      <pubDate>Fri, 08 Aug 2008 02:10:40 GMT</pubDate>
      <description>&lt;p&gt;
One of the original problems with the &lt;a href="http://www.garchibald.com/Blog/2008/08/02/BabySmashOnSilverlight.aspx"&gt;initial
port of BabySmash to Silverlight&lt;/a&gt; was loading the XAML storyboard animations for
UserControls. 
&lt;/p&gt;
&lt;p&gt;
In this post I will cover the changes required from a Silverlight point of view to
replace the RoutedEvent in the UserControl.Triggers section with either equivalent
XAML or C# code depending on your need. 
&lt;br&gt;
&lt;/p&gt;
&lt;h3&gt;Refactoring Routed Event Changes
&lt;/h3&gt;
&lt;p&gt;
XAML in 
&lt;abbr title="Windows Presentation Foundation"&gt;
WPF
&lt;/abbr&gt;
allows tools such as Expression Blend to define triggered events without the use of
code. In the original BabySmash XAML similar the following exists, which starts a
storyboard when the user control is loaded e.g.
&lt;/p&gt;
&lt;p&gt;
&amp;nbsp;
&lt;/p&gt;
&lt;!-- code formatted by http://manoli.net/csharpformat/ --&gt;
&lt;div class="csharpcode"&gt;&lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;UserControl.Triggers&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;    &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;EventTrigger&lt;/span&gt; &lt;span class="attr"&gt;RoutedEvent&lt;/span&gt;&lt;span class="kwrd"&gt;="FrameworkElement.Loaded"&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;        &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;BeginStoryboard&lt;/span&gt; &lt;span class="attr"&gt;Storyboard&lt;/span&gt;&lt;span class="kwrd"&gt;="{StaticResource
EyesSB}"&lt;/span&gt;&lt;span class="kwrd"&gt;/&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;    &lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;EventTrigger&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;UserControl.Triggers&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;
&amp;nbsp;
&lt;/p&gt;
&lt;p&gt;
As &lt;a href="http://silverlight.net/forums/t/12378.aspx"&gt;Shawn Wildermuth indicated
on the Silverlight forums&lt;/a&gt; Routed Events are not available in Silverlight 2.0 Beta
2 as a result the event trigger either needs to be deleted or commented out in the
XAML and alternative approach taken to achieve the same result. 
&lt;/p&gt;
&lt;p&gt;
Looking Richard Griffen's &lt;a href="http://blogs.conchango.com/richardgriffin/archive/2008/07/23/silverlight-tweening-adventures-with-baby-smash.aspx"&gt;Silverlight
Tweening Adventures with Baby Smash!&lt;/a&gt; MyTweenerTest it shows that the XAML can
easily be refactored by moving the animations inside a BeginStoryBoard element. Hence
not require the use of the Loaded event e.g.
&lt;/p&gt;
&lt;p&gt;
&amp;nbsp;
&lt;/p&gt;
&lt;!-- code formatted by http://manoli.net/csharpformat/ --&gt;
&lt;div class="csharpcode"&gt;&lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;UserControl.Resources&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;    &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;BeginStoryboard&lt;/span&gt; &lt;span class="attr"&gt;x:Name&lt;/span&gt;&lt;span class="kwrd"&gt;="Begin"&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;        &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;Storyboard&lt;/span&gt; &lt;span class="attr"&gt;x:Name&lt;/span&gt;&lt;span class="kwrd"&gt;="EyesSB"&lt;/span&gt; &lt;span class="attr"&gt;RepeatBehavior&lt;/span&gt;&lt;span class="kwrd"&gt;="Forever"&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;            &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;DoubleAnimationUsingKeyFrames&lt;/span&gt; &lt;span class="attr"&gt;BeginTime&lt;/span&gt;&lt;span class="kwrd"&gt;="00:00:00"&lt;/span&gt; &lt;span class="attr"&gt;Storyboard&lt;/span&gt;.&lt;span class="attr"&gt;TargetName&lt;/span&gt;&lt;span class="kwrd"&gt;="CircleEye1"&lt;/span&gt; &lt;span class="attr"&gt;Storyboard&lt;/span&gt;.&lt;span class="attr"&gt;TargetProperty&lt;/span&gt;&lt;span class="kwrd"&gt;="(UIElement.Opacity)"&lt;/span&gt; &lt;span class="attr"&gt;RepeatBehavior&lt;/span&gt;&lt;span class="kwrd"&gt;="Forever"&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;                &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;SplineDoubleKeyFrame&lt;/span&gt; &lt;span class="attr"&gt;KeyTime&lt;/span&gt;&lt;span class="kwrd"&gt;="00:00:02.1000000"&lt;/span&gt; &lt;span class="attr"&gt;Value&lt;/span&gt;&lt;span class="kwrd"&gt;="1"&lt;/span&gt;&lt;span class="kwrd"&gt;/&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;                &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;SplineDoubleKeyFrame&lt;/span&gt; &lt;span class="attr"&gt;KeyTime&lt;/span&gt;&lt;span class="kwrd"&gt;="00:00:02.1000000"&lt;/span&gt; &lt;span class="attr"&gt;Value&lt;/span&gt;&lt;span class="kwrd"&gt;="0"&lt;/span&gt;&lt;span class="kwrd"&gt;/&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;                &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;SplineDoubleKeyFrame&lt;/span&gt; &lt;span class="attr"&gt;KeyTime&lt;/span&gt;&lt;span class="kwrd"&gt;="00:00:02.300000"&lt;/span&gt; &lt;span class="attr"&gt;Value&lt;/span&gt;&lt;span class="kwrd"&gt;="0"&lt;/span&gt;&lt;span class="kwrd"&gt;/&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;                &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;SplineDoubleKeyFrame&lt;/span&gt; &lt;span class="attr"&gt;KeyTime&lt;/span&gt;&lt;span class="kwrd"&gt;="00:00:02.300000"&lt;/span&gt; &lt;span class="attr"&gt;Value&lt;/span&gt;&lt;span class="kwrd"&gt;="1"&lt;/span&gt;&lt;span class="kwrd"&gt;/&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;                &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;SplineDoubleKeyFrame&lt;/span&gt; &lt;span class="attr"&gt;KeyTime&lt;/span&gt;&lt;span class="kwrd"&gt;="00:00:7.300000"&lt;/span&gt; &lt;span class="attr"&gt;Value&lt;/span&gt;&lt;span class="kwrd"&gt;="1"&lt;/span&gt;&lt;span class="kwrd"&gt;/&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;            &lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;DoubleAnimationUsingKeyFrames&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;            &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;DoubleAnimationUsingKeyFrames&lt;/span&gt; &lt;span class="attr"&gt;BeginTime&lt;/span&gt;&lt;span class="kwrd"&gt;="00:00:00"&lt;/span&gt; &lt;span class="attr"&gt;Storyboard&lt;/span&gt;.&lt;span class="attr"&gt;TargetName&lt;/span&gt;&lt;span class="kwrd"&gt;="CircleEye2"&lt;/span&gt; &lt;span class="attr"&gt;Storyboard&lt;/span&gt;.&lt;span class="attr"&gt;TargetProperty&lt;/span&gt;&lt;span class="kwrd"&gt;="(UIElement.Opacity)"&lt;/span&gt; &lt;span class="attr"&gt;RepeatBehavior&lt;/span&gt;&lt;span class="kwrd"&gt;="Forever"&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;                &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;SplineDoubleKeyFrame&lt;/span&gt; &lt;span class="attr"&gt;KeyTime&lt;/span&gt;&lt;span class="kwrd"&gt;="00:00:02.1000000"&lt;/span&gt; &lt;span class="attr"&gt;Value&lt;/span&gt;&lt;span class="kwrd"&gt;="1"&lt;/span&gt;&lt;span class="kwrd"&gt;/&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;                &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;SplineDoubleKeyFrame&lt;/span&gt; &lt;span class="attr"&gt;KeyTime&lt;/span&gt;&lt;span class="kwrd"&gt;="00:00:02.1000000"&lt;/span&gt; &lt;span class="attr"&gt;Value&lt;/span&gt;&lt;span class="kwrd"&gt;="0"&lt;/span&gt;&lt;span class="kwrd"&gt;/&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;                &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;SplineDoubleKeyFrame&lt;/span&gt; &lt;span class="attr"&gt;KeyTime&lt;/span&gt;&lt;span class="kwrd"&gt;="00:00:02.300000"&lt;/span&gt; &lt;span class="attr"&gt;Value&lt;/span&gt;&lt;span class="kwrd"&gt;="0"&lt;/span&gt;&lt;span class="kwrd"&gt;/&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;                &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;SplineDoubleKeyFrame&lt;/span&gt; &lt;span class="attr"&gt;KeyTime&lt;/span&gt;&lt;span class="kwrd"&gt;="00:00:02.300000"&lt;/span&gt; &lt;span class="attr"&gt;Value&lt;/span&gt;&lt;span class="kwrd"&gt;="1"&lt;/span&gt;&lt;span class="kwrd"&gt;/&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;                &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;SplineDoubleKeyFrame&lt;/span&gt; &lt;span class="attr"&gt;KeyTime&lt;/span&gt;&lt;span class="kwrd"&gt;="00:00:7.300000"&lt;/span&gt; &lt;span class="attr"&gt;Value&lt;/span&gt;&lt;span class="kwrd"&gt;="1"&lt;/span&gt;&lt;span class="kwrd"&gt;/&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;            &lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;DoubleAnimationUsingKeyFrames&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;        &lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;Storyboard&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;    &lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;BeginStoryboard&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;&lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;UserControl.Resources&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;
&amp;nbsp;
&lt;/p&gt;
&lt;p&gt;
However I wanted to look at a way of using the code to apply dynamic changes to the
XAML and walkthough the process of refactoring.
&lt;/p&gt;
&lt;p&gt;
&amp;nbsp;
&lt;/p&gt;
&lt;h3&gt;Routed Event&amp;nbsp; - Replacement Using Code
&lt;/h3&gt;
&lt;p&gt;
&amp;nbsp;
&lt;/p&gt;
&lt;p&gt;
In the next few steps I will introduce a series of refactorings that create the code
behind and them reduce the amount of code that needs to be repeated.
&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
The first refactoring defines a Loaded event in each of the shapes (e.g. Circle, Heart,
Hexagon, Rectangle, Square, Star, Trapezoid and Triangle) 
&lt;/li&gt;
&lt;li&gt;
The next refactoring reduces the code by using a extension method to remove the need
for the duplicated event handler implementation. 
&lt;/li&gt;
&lt;li&gt;
The third refactoring introduces a convention that the eyes animation storyboard should
be called EyesSB and applied to UserControls.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;
Overall the aim of the refactorings are to:
&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
Reduce the amount of code that has to be implemented by developers or designers. 
&lt;/li&gt;
&lt;li&gt;
Provides documented implementation of how the storyboard is applied. 
&lt;/li&gt;
&lt;li&gt;
Move the code required into a separate class that can be unit tested. 
&lt;/li&gt;
&lt;li&gt;
Allows new shapes to be created that do not require any additional code to be written.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;
Overall the effect is that the Routed event section is no longer requires in each
of the XAML files. 
&lt;/p&gt;
&lt;p&gt;
&amp;nbsp;
&lt;/p&gt;
&lt;h4&gt;Refactoring 1 - Inline Event
&lt;/h4&gt;
&lt;p&gt;
&amp;nbsp;
&lt;/p&gt;
&lt;p&gt;
Change the code behind for each of the shapes and add a ShapedLoaded event e.g.
&lt;/p&gt;
&lt;p&gt;
&amp;nbsp;
&lt;/p&gt;
&lt;!-- code formatted by http://manoli.net/csharpformat/ --&gt;
&lt;div class="csharpcode"&gt;&lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;public&lt;/span&gt; CoolHeart()&lt;/pre&gt;&lt;pre&gt;{&lt;/pre&gt;&lt;pre class="alt"&gt; &lt;span class="kwrd"&gt;this&lt;/span&gt;.InitializeComponent();&lt;/pre&gt;&lt;pre&gt; &lt;span class="kwrd"&gt;this&lt;/span&gt;.Loaded
+= ShapeLoaded;&lt;/pre&gt;&lt;pre class="alt"&gt;}&lt;/pre&gt;&lt;pre&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; ShapeLoaded(&lt;span class="kwrd"&gt;object&lt;/span&gt; sender,
RoutedEventArgs e)&lt;/pre&gt;&lt;pre&gt;{&lt;/pre&gt;&lt;pre class="alt"&gt;  var eyes = FindName(&lt;span class="str"&gt;"EyesSB"&lt;/span&gt;) &lt;span class="kwrd"&gt;as&lt;/span&gt; Storyboard;&lt;/pre&gt;&lt;pre&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre class="alt"&gt;  &lt;span class="kwrd"&gt;if&lt;/span&gt; (eyes
!= &lt;span class="kwrd"&gt;null&lt;/span&gt;)&lt;/pre&gt;&lt;pre&gt;      eyes.Begin();&lt;/pre&gt;&lt;pre class="alt"&gt;}&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;
&amp;nbsp;
&lt;/p&gt;
&lt;h4&gt;Refactoring 2 - Extension Method
&lt;/h4&gt;
&lt;p&gt;
&amp;nbsp;
&lt;/p&gt;
&lt;p&gt;
To aid with readability and reuse across all the shapes the refactoring above can
further be improved. By moving to an extension method in a separate class. As a result
we can remove ShapeLoaded from each shape e.g.
&lt;/p&gt;
&lt;p&gt;
&amp;nbsp;
&lt;/p&gt;
&lt;!-- code formatted by http://manoli.net/csharpformat/ --&gt;
&lt;div class="csharpcode"&gt;&lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;public&lt;/span&gt; CoolHeart()&lt;/pre&gt;&lt;pre&gt;{&lt;/pre&gt;&lt;pre class="alt"&gt; &lt;span class="kwrd"&gt;this&lt;/span&gt;.InitializeComponent();&lt;/pre&gt;&lt;pre&gt; &lt;span class="kwrd"&gt;this&lt;/span&gt;.Loaded
+= &lt;span class="kwrd"&gt;this&lt;/span&gt;.BeginStoryBoardAnimation(&lt;span class="str"&gt;"EyesSB"&lt;/span&gt;);&lt;/pre&gt;&lt;pre class="alt"&gt;}&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;
&amp;nbsp;
&lt;/p&gt;
&lt;p&gt;
With the definition of the extension method as follows
&lt;/p&gt;
&lt;p&gt;
&amp;nbsp;
&lt;/p&gt;
&lt;!-- code formatted by http://manoli.net/csharpformat/ --&gt;
&lt;div class="csharpcode"&gt;&lt;pre class="alt"&gt;&lt;span class="rem"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;&lt;span class="rem"&gt;///
Helper class for XAML that performs common tasks&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;&lt;span class="rem"&gt;///
&amp;lt;/summary&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;&lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;static&lt;/span&gt; &lt;span class="kwrd"&gt;class&lt;/span&gt; XamlHelper&lt;/pre&gt;&lt;pre class="alt"&gt;{&lt;/pre&gt;&lt;pre&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre class="alt"&gt;    &lt;span class="rem"&gt;///
&amp;lt;summary&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;    &lt;span class="rem"&gt;/// Begins the named storyboard
animation.&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;    &lt;span class="rem"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;    &lt;span class="rem"&gt;///
&amp;lt;remarks&amp;gt;No exception if thrown if the storyboard is not found&amp;lt;/remarks&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;    &lt;span class="rem"&gt;///
&amp;lt;param name="control"&amp;gt;The control that contains the storyboard&amp;lt;/param&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;    &lt;span class="rem"&gt;///
&amp;lt;param name="storyboardName"&amp;gt;Name of the storyboard to be started&amp;lt;/param&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;    &lt;span class="rem"&gt;///
&amp;lt;returns&amp;gt;An event handler instance &amp;lt;/returns&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;    &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;static&lt;/span&gt; RoutedEventHandler
BeginStoryBoardAnimation(&lt;span class="kwrd"&gt;this&lt;/span&gt; UserControl control, &lt;span class="kwrd"&gt;string&lt;/span&gt; storyboardName)&lt;/pre&gt;&lt;pre class="alt"&gt;    {&lt;/pre&gt;&lt;pre&gt;        &lt;span class="kwrd"&gt;return&lt;/span&gt; (source,
args) =&amp;gt;&lt;/pre&gt;&lt;pre class="alt"&gt;                   {&lt;/pre&gt;&lt;pre&gt;                       var eyes = control.FindName(storyboardName) &lt;span class="kwrd"&gt;as&lt;/span&gt; Storyboard;&lt;/pre&gt;&lt;pre class="alt"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre&gt;                       &lt;span class="kwrd"&gt;if&lt;/span&gt; (eyes
!= &lt;span class="kwrd"&gt;null&lt;/span&gt;)&lt;/pre&gt;&lt;pre class="alt"&gt;                           eyes.Begin();&lt;/pre&gt;&lt;pre&gt;                   };&lt;/pre&gt;&lt;pre class="alt"&gt;    }&lt;/pre&gt;&lt;pre&gt;}&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;
&amp;nbsp;
&lt;/p&gt;
&lt;h4&gt;Refactoring 3 - Naming Convention
&lt;/h4&gt;
&lt;p&gt;
&amp;nbsp;
&lt;/p&gt;
&lt;p&gt;
Remove the initialisation of the Loaded event from the constructor of each shape and
update the FigureGenerator so that the EyesSB is started if it exists.
&lt;/p&gt;
&lt;p&gt;
&amp;nbsp;
&lt;/p&gt;
&lt;!-- code formatted by http://manoli.net/csharpformat/ --&gt;
&lt;div class="csharpcode"&gt;&lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;static&lt;/span&gt; UserControl
NewUserControlFrom(FigureTemplate template)&lt;/pre&gt;&lt;pre&gt;{&lt;/pre&gt;&lt;pre class="alt"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre&gt;    UserControl retVal = &lt;span class="kwrd"&gt;null&lt;/span&gt;;&lt;/pre&gt;&lt;pre class="alt"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre&gt;    &lt;span class="rem"&gt;//We'll
wait for Hardware Accelerated Shader Effects in SP1&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;    &lt;span class="kwrd"&gt;if&lt;/span&gt; (template.Letter.Length
== 1 &amp;amp;&amp;amp; Char.IsLetterOrDigit(template.Letter[0]))&lt;/pre&gt;&lt;pre&gt;    {&lt;/pre&gt;&lt;pre class="alt"&gt;          retVal = &lt;span class="kwrd"&gt;new&lt;/span&gt; CoolLetter(template.Fill,
template.Letter);&lt;/pre&gt;&lt;pre&gt;    }&lt;/pre&gt;&lt;pre class="alt"&gt;    &lt;span class="kwrd"&gt;else&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;    {&lt;/pre&gt;&lt;pre class="alt"&gt;          retVal = template.GeneratorFunc(template.Fill);&lt;/pre&gt;&lt;pre&gt;          retVal.StartStoryboardAnimation(&lt;span class="str"&gt;"EyesSB"&lt;/span&gt;);&lt;/pre&gt;&lt;pre class="alt"&gt;    }&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;
&amp;nbsp;
&lt;/p&gt;
&lt;p&gt;
With the updated definition of the extension method
&lt;/p&gt;
&lt;p&gt;
&amp;nbsp;
&lt;/p&gt;
&lt;!-- code formatted by http://manoli.net/csharpformat/ --&gt;
&lt;div class="csharpcode"&gt;
&lt;pre class="alt"&gt;&lt;span class="rem"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;&lt;/pre&gt;
&lt;pre&gt;&amp;nbsp;&lt;/pre&gt;
&lt;pre class="alt"&gt;&lt;span class="rem"&gt;/// Helper class for XAML that performs common
tasks&lt;/span&gt;&lt;/pre&gt;
&lt;pre&gt;&amp;nbsp;&lt;/pre&gt;
&lt;pre class="alt"&gt;&lt;span class="rem"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;&lt;/pre&gt;
&lt;pre&gt;&amp;nbsp;&lt;/pre&gt;
&lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;static&lt;/span&gt; &lt;span class="kwrd"&gt;class&lt;/span&gt; XamlHelper&lt;/pre&gt;
&lt;pre&gt;&amp;nbsp;&lt;/pre&gt;
&lt;pre class="alt"&gt;{&lt;/pre&gt;
&lt;pre&gt;&amp;nbsp;&lt;/pre&gt;
&lt;pre class="alt"&gt; &lt;/pre&gt;
&lt;pre&gt;&amp;nbsp;&lt;/pre&gt;
&lt;pre class="alt"&gt;&lt;span class="rem"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;&lt;/pre&gt;
&lt;pre&gt;&amp;nbsp;&lt;/pre&gt;
&lt;pre class="alt"&gt;&lt;span class="rem"&gt;/// Begins the named story board animation.&lt;/span&gt;&lt;/pre&gt;
&lt;pre&gt;&amp;nbsp;&lt;/pre&gt;
&lt;pre class="alt"&gt;&lt;span class="rem"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;&lt;/pre&gt;
&lt;pre&gt;&amp;nbsp;&lt;/pre&gt;
&lt;pre class="alt"&gt;&lt;span class="rem"&gt;/// &amp;lt;remarks&amp;gt;No exception if thrown if the
story board is not found&amp;lt;/remarks&amp;gt;&lt;/span&gt;&lt;/pre&gt;
&lt;pre&gt;&amp;nbsp;&lt;/pre&gt;
&lt;pre class="alt"&gt;&lt;span class="rem"&gt;/// &amp;lt;param name="control"&amp;gt;The control that
contains the story board&amp;lt;/param&amp;gt;&lt;/span&gt;&lt;/pre&gt;
&lt;pre&gt;&amp;nbsp;&lt;/pre&gt;
&lt;pre class="alt"&gt;&lt;span class="rem"&gt;/// &amp;lt;param name="storyboardName"&amp;gt;Name of
the storyboard to be started&amp;lt;/param&amp;gt;&lt;/span&gt;&lt;/pre&gt;
&lt;pre&gt;&amp;nbsp;&lt;/pre&gt;
&lt;pre class="alt"&gt;&lt;span class="rem"&gt;/// &amp;lt;returns&amp;gt;An event handler instance &amp;lt;/returns&amp;gt;&lt;/span&gt;&lt;/pre&gt;
&lt;pre&gt;&amp;nbsp;&lt;/pre&gt;
&lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;static&lt;/span&gt; RoutedEventHandler
BeginStoryBoardAnimation(&lt;span class="kwrd"&gt;this&lt;/span&gt; UserControl control, &lt;span class="kwrd"&gt;string&lt;/span&gt; storyboardName)&lt;/pre&gt;
&lt;pre&gt;&amp;nbsp;&lt;/pre&gt;
&lt;pre class="alt"&gt;{&lt;/pre&gt;
&lt;pre&gt;&amp;nbsp;&lt;/pre&gt;
&lt;pre class="alt"&gt;    &lt;span class="kwrd"&gt;return&lt;/span&gt; (source, args) =&amp;gt; StartStoryboardAnimation(control,
storyboardName);&lt;/pre&gt;
&lt;pre&gt;&amp;nbsp;&lt;/pre&gt;
&lt;pre class="alt"&gt;}&lt;/pre&gt;
&lt;pre&gt;&amp;nbsp;&lt;/pre&gt;
&lt;pre class="alt"&gt; &lt;/pre&gt;
&lt;pre&gt;&amp;nbsp;&lt;/pre&gt;
&lt;pre class="alt"&gt;&lt;span class="rem"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;&lt;/pre&gt;
&lt;pre&gt;&amp;nbsp;&lt;/pre&gt;
&lt;pre class="alt"&gt;&lt;span class="rem"&gt;/// Starts the named story board animation.&lt;/span&gt;&lt;/pre&gt;
&lt;pre&gt;&amp;nbsp;&lt;/pre&gt;
&lt;pre class="alt"&gt;&lt;span class="rem"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;&lt;/pre&gt;
&lt;pre&gt;&amp;nbsp;&lt;/pre&gt;
&lt;pre class="alt"&gt;&lt;span class="rem"&gt;/// &amp;lt;remarks&amp;gt;No exception if thrown if the
story board is not found&amp;lt;/remarks&amp;gt;&lt;/span&gt;&lt;/pre&gt;
&lt;pre&gt;&amp;nbsp;&lt;/pre&gt;
&lt;pre class="alt"&gt;&lt;span class="rem"&gt;/// &amp;lt;param name="control"&amp;gt;The control that
contains the story board&amp;lt;/param&amp;gt;&lt;/span&gt;&lt;/pre&gt;
&lt;pre&gt;&amp;nbsp;&lt;/pre&gt;
&lt;pre class="alt"&gt;&lt;span class="rem"&gt;/// &amp;lt;param name="storyboardName"&amp;gt;Name of
the storyboard to be started&amp;lt;/param&amp;gt;&lt;/span&gt;&lt;/pre&gt;
&lt;pre&gt;&amp;nbsp;&lt;/pre&gt;
&lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;static&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; StartStoryboardAnimation(&lt;span class="kwrd"&gt;this&lt;/span&gt; UserControl
control, &lt;span class="kwrd"&gt;string&lt;/span&gt; storyboardName)&lt;/pre&gt;
&lt;pre&gt;&amp;nbsp;&lt;/pre&gt;
&lt;pre class="alt"&gt;{&lt;/pre&gt;
&lt;pre&gt;&amp;nbsp;&lt;/pre&gt;
&lt;pre class="alt"&gt;    var eyes = control.FindName(storyboardName) &lt;span class="kwrd"&gt;as&lt;/span&gt; Storyboard;&lt;/pre&gt;
&lt;pre&gt;&amp;nbsp;&lt;/pre&gt;
&lt;pre class="alt"&gt; &lt;/pre&gt;
&lt;pre&gt;&amp;nbsp;&lt;/pre&gt;
&lt;pre class="alt"&gt;    &lt;span class="kwrd"&gt;if&lt;/span&gt; (eyes != &lt;span class="kwrd"&gt;null&lt;/span&gt;)&lt;/pre&gt;
&lt;pre&gt;&amp;nbsp;&lt;/pre&gt;
&lt;pre class="alt"&gt;        eyes.Begin();&lt;/pre&gt;
&lt;pre&gt;&amp;nbsp;&lt;/pre&gt;
&lt;pre class="alt"&gt;}&lt;/pre&gt;
&lt;/div&gt;
&lt;br&gt;
&lt;img width="0" height="0" src="http://www.garchibald.com/blog/aggbug.ashx?id=ba69bc68-9ccd-4e9f-8b1b-b94b10b992c9" /&gt;</description>
      <comments>http://www.garchibald.com/blog/CommentView,guid,ba69bc68-9ccd-4e9f-8b1b-b94b10b992c9.aspx</comments>
      <category>silverlight</category>
      <category>babysmash</category>
    </item>
    <item>
      <trackback:ping>http://www.garchibald.com/blog/Trackback.aspx?guid=4c815528-e89f-4abc-adde-c655cb403ae8</trackback:ping>
      <pingback:server>http://www.garchibald.com/blog/pingback.aspx</pingback:server>
      <pingback:target>http://www.garchibald.com/blog/PermaLink,guid,4c815528-e89f-4abc-adde-c655cb403ae8.aspx</pingback:target>
      <dc:creator>Grant Archibald</dc:creator>
      <wfw:comment>http://www.garchibald.com/blog/CommentView,guid,4c815528-e89f-4abc-adde-c655cb403ae8.aspx</wfw:comment>
      <wfw:commentRss>http://www.garchibald.com/blog/SyndicationService.asmx/GetEntryCommentsRss?guid=4c815528-e89f-4abc-adde-c655cb403ae8</wfw:commentRss>
      <body xmlns="http://www.w3.org/1999/xhtml">After my <a href="http://www.garchibald.com/Blog/2008/08/02/BabySmashOnSilverlight.aspx">initial
port</a>, alpha-numeric letters could not be rendered as the FormattedText object
is not available within Silverlight. This left me with the following options: 
<ul><li>
Call a web service to dynamically convert the letters to the associated XAML path
for a defines font. 
</li><li>
Create a small WPF application that generates the XAML path geometry and embed this
in a C# dictionary object within the code.</li></ul><p>
The second of the two options was the quickest (code snippet below). In the future
another enhancement could look at creating web service or using a xap file with different
fonts.
</p><p>
The next piece to the process was to load the path data into a Geometry instance.
Using Alex Golesh's <a href="http://blogs.microsoft.co.il/blogs/alex_golesh/archive/2008/06/18/path-data-dependency-property-initialization-from-string-take-2.aspx">PathFigureCollectionConverter</a> the
shapes are now loaded and the updated version now supports generation of letters.
One problem I have now if that the closed in shapes are not rendering properly i.e.
the O is a solid oval.
</p><p></p><!-- code formatted by http://manoli.net/csharpformat/ --><div class="csharpcode"><pre class="alt">var code = <span class="kwrd">new</span> StringBuilder(); </pre><pre> </pre><pre class="alt">code.Append(<span class="str">"var arial = new Dictionary&lt;string,
string&gt;();\r\n"</span>);</pre><pre>var lettersToCreate = <span class="str">"ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"</span>;</pre><pre class="alt"></pre><pre><span class="kwrd">for</span> (var i = 0;i &lt; lettersToCreate.Length; i++)</pre><pre class="alt">{</pre><pre>    var letter = lettersToCreate.Substring(i, 1);</pre><pre class="alt">    var fText = <span class="kwrd">new</span> FormattedText(</pre><pre>    letter,</pre><pre class="alt">    CultureInfo.CurrentCulture,</pre><pre>    FlowDirection.LeftToRight,</pre><pre class="alt"><span class="kwrd">new</span> Typeface(</pre><pre><span class="kwrd">new</span> FontFamily(<span class="str">"Arial"</span>),</pre><pre class="alt">        FontStyles.Normal,</pre><pre>        FontWeights.Heavy,</pre><pre class="alt">        FontStretches.Normal),</pre><pre>    300,</pre><pre class="alt">    Brushes.Black</pre><pre>    );</pre><pre class="alt">    var g = fText.BuildGeometry(<span class="kwrd">new</span> Point(0,
0)).GetAsFrozen() <span class="kwrd">as</span> Geometry;</pre><pre><span class="rem">// Get the Path info from the geometry</span></pre><pre class="alt">    var p = g.GetFlattenedPathGeometry();</pre><pre></pre><pre class="alt">    code.Append(<span class="str">"arial.Add(\""</span> + letter
+ <span class="str">"\", \""</span> + p.ToString().Replace(<span class="str">"F1M"</span>, <span class="str">"M
"</span>) + <span class="str">"\");\r\n"</span>);</pre><pre>}</pre></div><img width="0" height="0" src="http://www.garchibald.com/blog/aggbug.ashx?id=4c815528-e89f-4abc-adde-c655cb403ae8" /></body>
      <title>BabySmash Silverlight Refactorings - Part 1 - Adding Letters</title>
      <guid isPermaLink="false">http://www.garchibald.com/blog/PermaLink,guid,4c815528-e89f-4abc-adde-c655cb403ae8.aspx</guid>
      <link>http://www.garchibald.com/blog/2008/08/04/BabySmashSilverlightRefactoringsPart1AddingLetters.aspx</link>
      <pubDate>Mon, 04 Aug 2008 12:35:13 GMT</pubDate>
      <description>After my &lt;a href="http://www.garchibald.com/Blog/2008/08/02/BabySmashOnSilverlight.aspx"&gt;initial
port&lt;/a&gt;, alpha-numeric letters could not be rendered as the FormattedText object
is not available within Silverlight. This left me with the following options: 
&lt;ul&gt;
&lt;li&gt;
Call a web service to dynamically convert the letters to the associated XAML path
for a defines font. 
&lt;/li&gt;
&lt;li&gt;
Create a small WPF application that generates the XAML path geometry and embed this
in a C# dictionary object within the code.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
The second of the two options was the quickest (code snippet below). In the future
another enhancement could look at creating web service or using a xap file with different
fonts.
&lt;/p&gt;
&lt;p&gt;
The next piece to the process was to load the path data into a Geometry instance.
Using Alex Golesh's &lt;a href="http://blogs.microsoft.co.il/blogs/alex_golesh/archive/2008/06/18/path-data-dependency-property-initialization-from-string-take-2.aspx"&gt;PathFigureCollectionConverter&lt;/a&gt; the
shapes are now loaded and the updated version now supports generation of letters.
One problem I have now if that the closed in shapes are not rendering properly i.e.
the O is a solid oval.
&lt;/p&gt;
&lt;p&gt;
&lt;/p&gt;
&lt;!-- code formatted by http://manoli.net/csharpformat/ --&gt;
&lt;div class="csharpcode"&gt;
&lt;pre class="alt"&gt;var code = &lt;span class="kwrd"&gt;new&lt;/span&gt; StringBuilder(); &lt;/pre&gt;
&lt;pre&gt;&amp;nbsp;&lt;/pre&gt;
&lt;pre class="alt"&gt;code.Append(&lt;span class="str"&gt;"var arial = new Dictionary&amp;lt;string,
string&amp;gt;();\r\n"&lt;/span&gt;);&lt;/pre&gt;
&lt;pre&gt;var lettersToCreate = &lt;span class="str"&gt;"ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"&lt;/span&gt;;&lt;/pre&gt;
&lt;pre class="alt"&gt; &lt;/pre&gt;
&lt;pre&gt;&lt;span class="kwrd"&gt;for&lt;/span&gt; (var i = 0;i &amp;lt; lettersToCreate.Length; i++)&lt;/pre&gt;
&lt;pre class="alt"&gt;{&lt;/pre&gt;
&lt;pre&gt;    var letter = lettersToCreate.Substring(i, 1);&lt;/pre&gt;
&lt;pre class="alt"&gt;    var fText = &lt;span class="kwrd"&gt;new&lt;/span&gt; FormattedText(&lt;/pre&gt;
&lt;pre&gt;    letter,&lt;/pre&gt;
&lt;pre class="alt"&gt;    CultureInfo.CurrentCulture,&lt;/pre&gt;
&lt;pre&gt;    FlowDirection.LeftToRight,&lt;/pre&gt;
&lt;pre class="alt"&gt;    &lt;span class="kwrd"&gt;new&lt;/span&gt; Typeface(&lt;/pre&gt;
&lt;pre&gt;        &lt;span class="kwrd"&gt;new&lt;/span&gt; FontFamily(&lt;span class="str"&gt;"Arial"&lt;/span&gt;),&lt;/pre&gt;
&lt;pre class="alt"&gt;        FontStyles.Normal,&lt;/pre&gt;
&lt;pre&gt;        FontWeights.Heavy,&lt;/pre&gt;
&lt;pre class="alt"&gt;        FontStretches.Normal),&lt;/pre&gt;
&lt;pre&gt;    300,&lt;/pre&gt;
&lt;pre class="alt"&gt;    Brushes.Black&lt;/pre&gt;
&lt;pre&gt;    );&lt;/pre&gt;
&lt;pre class="alt"&gt;    var g = fText.BuildGeometry(&lt;span class="kwrd"&gt;new&lt;/span&gt; Point(0,
0)).GetAsFrozen() &lt;span class="kwrd"&gt;as&lt;/span&gt; Geometry;&lt;/pre&gt;
&lt;pre&gt;    &lt;span class="rem"&gt;// Get the Path info from the geometry&lt;/span&gt;&lt;/pre&gt;
&lt;pre class="alt"&gt;    var p = g.GetFlattenedPathGeometry();&lt;/pre&gt;
&lt;pre&gt; &lt;/pre&gt;
&lt;pre class="alt"&gt;    code.Append(&lt;span class="str"&gt;"arial.Add(\""&lt;/span&gt; + letter
+ &lt;span class="str"&gt;"\", \""&lt;/span&gt; + p.ToString().Replace(&lt;span class="str"&gt;"F1M"&lt;/span&gt;, &lt;span class="str"&gt;"M
"&lt;/span&gt;) + &lt;span class="str"&gt;"\");\r\n"&lt;/span&gt;);&lt;/pre&gt;
&lt;pre&gt;}&lt;/pre&gt;
&lt;/div&gt;
&lt;img width="0" height="0" src="http://www.garchibald.com/blog/aggbug.ashx?id=4c815528-e89f-4abc-adde-c655cb403ae8" /&gt;</description>
      <comments>http://www.garchibald.com/blog/CommentView,guid,4c815528-e89f-4abc-adde-c655cb403ae8.aspx</comments>
      <category>babysmash</category>
      <category>silverlight</category>
    </item>
    <item>
      <trackback:ping>http://www.garchibald.com/blog/Trackback.aspx?guid=7b3a2a3b-5740-4076-bc67-36d35831efb5</trackback:ping>
      <pingback:server>http://www.garchibald.com/blog/pingback.aspx</pingback:server>
      <pingback:target>http://www.garchibald.com/blog/PermaLink,guid,7b3a2a3b-5740-4076-bc67-36d35831efb5.aspx</pingback:target>
      <dc:creator>Grant Archibald</dc:creator>
      <wfw:comment>http://www.garchibald.com/blog/CommentView,guid,7b3a2a3b-5740-4076-bc67-36d35831efb5.aspx</wfw:comment>
      <wfw:commentRss>http://www.garchibald.com/blog/SyndicationService.asmx/GetEntryCommentsRss?guid=7b3a2a3b-5740-4076-bc67-36d35831efb5</wfw:commentRss>
      <slash:comments>2</slash:comments>
      <body xmlns="http://www.w3.org/1999/xhtml">
        <p>
After listening to Scott Hanselman's podcast on <a href="http://www.hanselman.com/blog/HanselminutesPodcast122BabySmash.aspx">BabySmash</a> I
wanted to have a go at creating a proof on concept Silverlight "BabySmash Web Edition".
I have embedded the <a href="http://www.garchibald.com/Blog/2008/08/02/BabySmashOnSilverlight.aspx#CurrentVersion">current
version</a> at the bottom of the page if you want to skip the technical discussion.
</p>
        <p>
The web edition has the following differences from the original <a href="http://www.babysmash.com/">BabySmash</a></p>
        <ul>
          <li>
Uses Silverlight 2.0 Beta 2 instead Windows Presentation Foundation application for
Windows XP/Vista. 
<ul><li>
Silverlight 2.0 download size much smaller (Around 4 MB) 
</li><li>
No need to install the .Net Framework 3.5 (Around 33MB). 
</li></ul></li>
          <li>
Smaller download size for the application (Currently around 450KB). 
</li>
          <li>
Hosted inside the web browser. 
</li>
          <li>
Can be run on all platforms supported by Silverlight 2.0 Beta 2 (i.e. Window XP/Vista
and Intel Mac's).</li>
        </ul>
        <p>
What are the limitations?
</p>
        <ul>
          <li>
Capturing system keys. Obviously limited to keys that the plugin  can capture. 
<ul><li>
For my son I use an external usb numeric keyboard similar to one made by <a href="http://www.targus.com/us/product_Details.asp?SKU=PAUK10U">Targus</a>.</li></ul></li>
          <li>
Mouse click limited to left click only. Right click brings up Silverlight Configuration</li>
        </ul>
        <p>
What is working?
</p>
        <ul>
          <li>
Keyboard and basic mouse integration. 
</li>
          <li>
Ability to play audio. 
</li>
          <li>
Basic options dialog UI.</li>
        </ul>
        <p>
What is missing?
</p>
        <ul>
          <li>
Animations (Character blinks &amp; fades). 
</li>
          <li>
Click animations. 
</li>
          <li>
ClickOnce deployment/updates - Not needed for web version. 
</li>
          <li>
Letters. 
</li>
          <li>
Loading and Saving options. 
</li>
          <li>
Text to speech. 
</li>
        </ul>
        <p>
Work yet to do:
</p>
        <ul>
          <li>
Suggest refactoring's to allow common WPF/Silverlight codebase. 
</li>
          <li>
Install experience for users that do not have Silverlight 2.0 installed. 
</li>
          <li>
Complete the options dialog. 
</li>
          <li>
Open the options dialog by clicking in each of the corners of the control. 
</li>
          <li>
Animations (Character Blinks &amp; fades). 
</li>
          <li>
Add support to display letters. 
</li>
          <li>
Add full screen mode. 
</li>
          <li>
Mouse scroll wheel support. 
</li>
          <li>
Test with experimental 2.0 Moonlight plugin. 
</li>
          <li>
Include sound files for letters and numbers. 
</li>
          <li>
Separate out sound files into another downloadable component.</li>
        </ul>
        <p>
So here is the current version. Any suggestions/feedback appreciated.
</p>
        <a name="CurrentVersion">
        </a>
        <p>
          <iframe style="border: 1px solid black;" src="http://silverlight.services.live.com/invoke/55752/babysmashweb/iframe.html" scrolling="no" width="700" frameborder="0" height="450">
          </iframe>
        </p>
        <a name="CurrentVersion">
        </a>
        <p>
          <a name="CurrentVersion">By using the application please note the </a>
          <a href="http://www.codeplex.com/babysmash/license">BabySmash
Licensing Terms</a>
        </p>
        <img width="0" height="0" src="http://www.garchibald.com/blog/aggbug.ashx?id=7b3a2a3b-5740-4076-bc67-36d35831efb5" />
      </body>
      <title>BabySmash on Silverlight</title>
      <guid isPermaLink="false">http://www.garchibald.com/blog/PermaLink,guid,7b3a2a3b-5740-4076-bc67-36d35831efb5.aspx</guid>
      <link>http://www.garchibald.com/blog/2008/08/02/BabySmashOnSilverlight.aspx</link>
      <pubDate>Sat, 02 Aug 2008 12:42:43 GMT</pubDate>
      <description>&lt;p&gt;
After listening to Scott Hanselman's podcast on &lt;a href="http://www.hanselman.com/blog/HanselminutesPodcast122BabySmash.aspx"&gt;BabySmash&lt;/a&gt; I
wanted to have a go at creating a proof on concept Silverlight "BabySmash Web Edition".
I have embedded the &lt;a href="http://www.garchibald.com/Blog/2008/08/02/BabySmashOnSilverlight.aspx#CurrentVersion"&gt;current
version&lt;/a&gt; at the bottom of the page if you want to skip the technical discussion.
&lt;/p&gt;
&lt;p&gt;
The web edition has the following differences from the original &lt;a href="http://www.babysmash.com/"&gt;BabySmash&lt;/a&gt;
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
Uses Silverlight 2.0 Beta 2 instead Windows Presentation Foundation application for
Windows XP/Vista. 
&lt;ul&gt;
&lt;li&gt;
Silverlight 2.0 download size much smaller (Around 4 MB) 
&lt;/li&gt;
&lt;li&gt;
No need to install the .Net Framework 3.5 (Around 33MB). 
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
Smaller download size for the application (Currently around 450KB). 
&lt;/li&gt;
&lt;li&gt;
Hosted inside the web browser. 
&lt;/li&gt;
&lt;li&gt;
Can be run on all platforms supported by Silverlight 2.0 Beta 2 (i.e. Window XP/Vista
and Intel Mac's).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
What are the limitations?
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
Capturing system keys. Obviously limited to keys that the plugin&amp;nbsp; can capture. 
&lt;ul&gt;
&lt;li&gt;
For my son I use an external usb numeric keyboard similar to one made by &lt;a href="http://www.targus.com/us/product_Details.asp?SKU=PAUK10U"&gt;Targus&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
Mouse click limited to left click only. Right click brings up Silverlight Configuration&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
What is working?
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
Keyboard and basic mouse integration. 
&lt;/li&gt;
&lt;li&gt;
Ability to play audio. 
&lt;/li&gt;
&lt;li&gt;
Basic options dialog UI.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
What is missing?
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
Animations (Character blinks &amp;amp; fades). 
&lt;/li&gt;
&lt;li&gt;
Click animations. 
&lt;/li&gt;
&lt;li&gt;
ClickOnce deployment/updates - Not needed for web version. 
&lt;/li&gt;
&lt;li&gt;
Letters. 
&lt;/li&gt;
&lt;li&gt;
Loading and Saving options. 
&lt;/li&gt;
&lt;li&gt;
Text to speech. 
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
Work yet to do:
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
Suggest refactoring's to allow common WPF/Silverlight codebase. 
&lt;/li&gt;
&lt;li&gt;
Install experience for users that do not have Silverlight 2.0 installed. 
&lt;/li&gt;
&lt;li&gt;
Complete the options dialog. 
&lt;/li&gt;
&lt;li&gt;
Open the options dialog by clicking in each of the corners of the control. 
&lt;/li&gt;
&lt;li&gt;
Animations (Character Blinks &amp;amp; fades). 
&lt;/li&gt;
&lt;li&gt;
Add support to display letters. 
&lt;/li&gt;
&lt;li&gt;
Add full screen mode. 
&lt;/li&gt;
&lt;li&gt;
Mouse scroll wheel support. 
&lt;/li&gt;
&lt;li&gt;
Test with experimental 2.0 Moonlight plugin. 
&lt;/li&gt;
&lt;li&gt;
Include sound files for letters and numbers. 
&lt;/li&gt;
&lt;li&gt;
Separate out sound files into another downloadable component.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
So here is the current version. Any suggestions/feedback appreciated.
&lt;/p&gt;
&lt;a name="CurrentVersion"&gt; &lt;/a&gt;
&lt;p&gt;
&lt;iframe style="border: 1px solid black;" src="http://silverlight.services.live.com/invoke/55752/babysmashweb/iframe.html" scrolling="no" width="700" frameborder="0" height="450"&gt;
&lt;/iframe&gt;
&lt;/p&gt;
&lt;a name="CurrentVersion"&gt; &lt;/a&gt;
&lt;p&gt;
&lt;a name="CurrentVersion"&gt;By using the application please note the &lt;/a&gt;&lt;a href="http://www.codeplex.com/babysmash/license"&gt;BabySmash
Licensing Terms&lt;/a&gt;
&lt;/p&gt;
&lt;img width="0" height="0" src="http://www.garchibald.com/blog/aggbug.ashx?id=7b3a2a3b-5740-4076-bc67-36d35831efb5" /&gt;</description>
      <comments>http://www.garchibald.com/blog/CommentView,guid,7b3a2a3b-5740-4076-bc67-36d35831efb5.aspx</comments>
      <category>babysmash</category>
      <category>silverlight</category>
    </item>
    <item>
      <trackback:ping>http://www.garchibald.com/blog/Trackback.aspx?guid=8f0a0a19-322d-461b-86ac-2de8ca4a1df3</trackback:ping>
      <pingback:server>http://www.garchibald.com/blog/pingback.aspx</pingback:server>
      <pingback:target>http://www.garchibald.com/blog/PermaLink,guid,8f0a0a19-322d-461b-86ac-2de8ca4a1df3.aspx</pingback:target>
      <dc:creator>Grant Archibald</dc:creator>
      <wfw:comment>http://www.garchibald.com/blog/CommentView,guid,8f0a0a19-322d-461b-86ac-2de8ca4a1df3.aspx</wfw:comment>
      <wfw:commentRss>http://www.garchibald.com/blog/SyndicationService.asmx/GetEntryCommentsRss?guid=8f0a0a19-322d-461b-86ac-2de8ca4a1df3</wfw:commentRss>
      <body xmlns="http://www.w3.org/1999/xhtml">
        <p>
Wanting to dip your toe in an see the current state of Moonlight under Linux or check
the compatibility of your Silverlight 1.0 application with Moonlight then read on.<br /><br />
Until recently to install Moonlight you had two choices: 
</p>
        <ol>
          <li>
Use the non multimedia version from the official <a href="http://www.go-mono.com/moonlight/">Moonlight
Download</a> page.</li>
          <li>
            <a href="http://www.mono-project.com/Moonlight">Compile from the source files</a> for
full Moonlight multimedia support.</li>
        </ol>
        <p>
Fortunately there is a precompiled package on the Third Party openSUSE site that allows
a relatively easy install process for openSUSE. In this post I am going to walk through
the process of setting up the current <a href="http://www.mono-project.com/news/archive/2008/Jul-02.html">Moonlight
Build 0.7</a> that is available as a 1 Click install within OpenSUSE 10.2 or newer
using Microsoft Virtual PC 2007.
</p>
        <p>
        </p>
The main steps in the process are:<br /><ul><li>
Download <a href="http://en.opensuse.org/Download">openSUSE LiveCD Version 11</a> from
the download site. For this post I have used the KDE version. 
</li><li>
Install of the Live CD onto the Virtual Hard Disk. 
</li><li>
Optional update of network settings for DNS server. This is only required if you cannot
access the Internet by default from within the virtual client. 
</li><li>
Online update to get security updates and latest Version 3.0 Firefox browser. 
</li><li>
Install of <a href="http://packman.links2linux.org/package/libmoon">Moonlight from
PackMan</a> using 1-Click install. 
</li><li>
Install of <a href="http://en.opensuse.org/Optimal_Use_of_MS_TrueType_Core_Fonts_for_a_KDE_Desktop_on_SuSE">Microsoft
TrueType fonts</a> to ensure correct rendering in the browser.<br /></li></ul><p></p><script type="text/javascript">
	window.addEvent('domready', function() {
		document.moonlightInstallGallerySet = new gallerySet($('moonlightInstallGallerySet'), {
		timed: false,
		embedLinks: false
		});
	});
	</script><div id="content"><div id="moonlightInstallGallerySet" class="galleryContainer"><div id="gallery01VPCInstall" class="galleryElement"><h2>01-VPC Install
</h2><div class="imageElement"><h3>01-Virtual PC-Setup New
</h3><p></p><a class="open" title="open image" href="#"></a><img class="full" src="http://www.garchibald.com/images/MoonlightInstall/01-VPCInstall/01-VirtualPC-SetupNew.jpg" /><img class="thumbnail" src="http://www.garchibald.com/images/MoonlightInstall/01-VPCInstall/01-VirtualPC-SetupNew-Small.jpg" /></div><div class="imageElement"><h3>02-Create A Virtual Machine
</h3><p></p><a class="open" title="open image" href="#"></a><img class="full" src="http://www.garchibald.com/images/MoonlightInstall/01-VPCInstall/02-CreateAVirtualMachine.jpg" /><img class="thumbnail" src="http://www.garchibald.com/images/MoonlightInstall/01-VPCInstall/02-CreateAVirtualMachine-Small.jpg" /></div><div class="imageElement"><h3>03-Name The Machine
</h3><p></p><a class="open" title="open image" href="#"></a><img class="full" src="http://www.garchibald.com/images/MoonlightInstall/01-VPCInstall/03-NameTheMachine.jpg" /><img class="thumbnail" src="http://www.garchibald.com/images/MoonlightInstall/01-VPCInstall/03-NameTheMachine-Small.jpg" /></div><div class="imageElement"><h3>04-Select Other Operating System
</h3><p></p><a class="open" title="open image" href="#"></a><img class="full" src="http://www.garchibald.com/images/MoonlightInstall/01-VPCInstall/04-SelectOtherOperatingSystem.jpg" /><img class="thumbnail" src="http://www.garchibald.com/images/MoonlightInstall/01-VPCInstall/04-SelectOtherOperatingSystem-Small.jpg" /></div><div class="imageElement"><h3>05-Finish To Complete
</h3><p></p><a class="open" title="open image" href="#"></a><img class="full" src="http://www.garchibald.com/images/MoonlightInstall/01-VPCInstall/05-FinishToComplete.jpg" /><img class="thumbnail" src="http://www.garchibald.com/images/MoonlightInstall/01-VPCInstall/05-FinishToComplete-Small.jpg" /></div><div class="imageElement"><h3>06-Start The Virtual Machine
</h3><p></p><a class="open" title="open image" href="#"></a><img class="full" src="http://www.garchibald.com/images/MoonlightInstall/01-VPCInstall/06-StartTheVirtualMachine.jpg" /><img class="thumbnail" src="http://www.garchibald.com/images/MoonlightInstall/01-VPCInstall/06-StartTheVirtualMachine-Small.jpg" /></div><div class="imageElement"><h3>07-Capture The Live Cd Iso
</h3><p></p><a class="open" title="open image" href="#"></a><img class="full" src="http://www.garchibald.com/images/MoonlightInstall/01-VPCInstall/07-CaptureTheLiveCdIso.jpg" /><img class="thumbnail" src="http://www.garchibald.com/images/MoonlightInstall/01-VPCInstall/07-CaptureTheLiveCdIso-Small.jpg" /></div><div class="imageElement"><h3>08-Start Open SUSE 11
</h3><p></p><a class="open" title="open image" href="#"></a><img class="full" src="http://www.garchibald.com/images/MoonlightInstall/01-VPCInstall/08-StartOpenSUSE11.jpg" /><img class="thumbnail" src="http://www.garchibald.com/images/MoonlightInstall/01-VPCInstall/08-StartOpenSUSE11-Small.jpg" /></div><div class="imageElement"><h3>09-Close Welcome Dialog
</h3><p></p><a class="open" title="open image" href="#"></a><img class="full" src="http://www.garchibald.com/images/MoonlightInstall/01-VPCInstall/09-CloseWelcomeDialog.jpg" /><img class="thumbnail" src="http://www.garchibald.com/images/MoonlightInstall/01-VPCInstall/09-CloseWelcomeDialog-Small.jpg" /></div><div class="imageElement"><h3>10-Select Install From Desktop
</h3><p></p><a class="open" title="open image" href="#"></a><img class="full" src="http://www.garchibald.com/images/MoonlightInstall/01-VPCInstall/10-SelectInstallFromDesktop.jpg" /><img class="thumbnail" src="http://www.garchibald.com/images/MoonlightInstall/01-VPCInstall/10-SelectInstallFromDesktop-Small.jpg" /></div><div class="imageElement"><h3>11-Agree To License Terms And Click Next
</h3><p></p><a class="open" title="open image" href="#"></a><img class="full" src="http://www.garchibald.com/images/MoonlightInstall/01-VPCInstall/11-AgreeToLicenseTermsAndClickNext.jpg" /><img class="thumbnail" src="http://www.garchibald.com/images/MoonlightInstall/01-VPCInstall/11-AgreeToLicenseTermsAndClickNext-Small.jpg" /></div><div class="imageElement"><h3>12-Select Timezone And Next
</h3><p></p><a class="open" title="open image" href="#"></a><img class="full" src="http://www.garchibald.com/images/MoonlightInstall/01-VPCInstall/12-SelectTimezoneAndNext.jpg" /><img class="thumbnail" src="http://www.garchibald.com/images/MoonlightInstall/01-VPCInstall/12-SelectTimezoneAndNext-Small.jpg" /></div><div class="imageElement"><h3>13-Accept Default Partitions
</h3><p></p><a class="open" title="open image" href="#"></a><img class="full" src="http://www.garchibald.com/images/MoonlightInstall/01-VPCInstall/13-AcceptDefaultPartitions.jpg" /><img class="thumbnail" src="http://www.garchibald.com/images/MoonlightInstall/01-VPCInstall/13-AcceptDefaultPartitions-Small.jpg" /></div><div class="imageElement"><h3>14-Enter User Name And Password
</h3><p></p><a class="open" title="open image" href="#"></a><img class="full" src="http://www.garchibald.com/images/MoonlightInstall/01-VPCInstall/14-EnterUserNameAndPassword.jpg" /><img class="thumbnail" src="http://www.garchibald.com/images/MoonlightInstall/01-VPCInstall/14-EnterUserNameAndPassword-Small.jpg" /></div><div class="imageElement"><h3>15-Review Install And Click Install
</h3><p></p><a class="open" title="open image" href="#"></a><img class="full" src="http://www.garchibald.com/images/MoonlightInstall/01-VPCInstall/15-ReviewInstallAndClickInstall.jpg" /><img class="thumbnail" src="http://www.garchibald.com/images/MoonlightInstall/01-VPCInstall/15-ReviewInstallAndClickInstall-Small.jpg" /></div><div class="imageElement"><h3>16-Click Install To Confirm
</h3><p></p><a class="open" title="open image" href="#"></a><img class="full" src="http://www.garchibald.com/images/MoonlightInstall/01-VPCInstall/16-ClickInstallToConfirm.jpg" /><img class="thumbnail" src="http://www.garchibald.com/images/MoonlightInstall/01-VPCInstall/16-ClickInstallToConfirm-Small.jpg" /></div><div class="imageElement"><h3>17-Click OK And Release Iso
</h3><p></p><a class="open" title="open image" href="#"></a><img class="full" src="http://www.garchibald.com/images/MoonlightInstall/01-VPCInstall/17-ClickOKAndReleaseIso.jpg" /><img class="thumbnail" src="http://www.garchibald.com/images/MoonlightInstall/01-VPCInstall/17-ClickOKAndReleaseIso-Small.jpg" /></div><div class="imageElement"><h3>18-Next To Reboot
</h3><p></p><a class="open" title="open image" href="#"></a><img class="full" src="http://www.garchibald.com/images/MoonlightInstall/01-VPCInstall/18-NextToReboot.jpg" /><img class="thumbnail" src="http://www.garchibald.com/images/MoonlightInstall/01-VPCInstall/18-NextToReboot-Small.jpg" /></div><div class="imageElement"><h3>19-Will Startup For First Time Time
</h3><p></p><a class="open" title="open image" href="#"></a><img class="full" src="http://www.garchibald.com/images/MoonlightInstall/01-VPCInstall/19-WillStartupForFirstTimeTime.jpg" /><img class="thumbnail" src="http://www.garchibald.com/images/MoonlightInstall/01-VPCInstall/19-WillStartupForFirstTimeTime-Small.jpg" /></div><div class="imageElement"><h3>20-Close Dialog
</h3><p></p><a class="open" title="open image" href="#"></a><img class="full" src="http://www.garchibald.com/images/MoonlightInstall/01-VPCInstall/20-CloseDialog.jpg" /><img class="thumbnail" src="http://www.garchibald.com/images/MoonlightInstall/01-VPCInstall/20-CloseDialog-Small.jpg" /></div></div><div id="gallery02NetworkSetup(Optional)" class="galleryElement"><h2>02-Network Setup (Optional)
</h2><div class="imageElement"><h3>01-Select YAST To Administer Settings
</h3><p></p><a class="open" title="open image" href="#"></a><img class="full" src="http://www.garchibald.com/images/MoonlightInstall/02-NetworkSetup%20%28Optional%29/01-SelectYASTToAdministerSettings.jpg" /><img class="thumbnail" src="http://www.garchibald.com/images/MoonlightInstall/02-NetworkSetup%20%28Optional%29/01-SelectYASTToAdministerSettings-Small.jpg" /></div><div class="imageElement"><h3>02-Enter Password Setup Earlier For Admin User
</h3><p></p><a class="open" title="open image" href="#"></a><img class="full" src="http://www.garchibald.com/images/MoonlightInstall/02-NetworkSetup%20%28Optional%29/02-EnterPasswordSetupEarlierForAdminUser.jpg" /><img class="thumbnail" src="http://www.garchibald.com/images/MoonlightInstall/02-NetworkSetup%20%28Optional%29/02-EnterPasswordSetupEarlierForAdminUser-Small.jpg" /></div><div class="imageElement"><h3>03-Select Network Devices And Click On Network settings
</h3><p></p><a class="open" title="open image" href="#"></a><img class="full" src="http://www.garchibald.com/images/MoonlightInstall/02-NetworkSetup%20%28Optional%29/03-SelectNetworkDevicesAndClickOnNetworksettings.jpg" /><img class="thumbnail" src="http://www.garchibald.com/images/MoonlightInstall/02-NetworkSetup%20%28Optional%29/03-SelectNetworkDevicesAndClickOnNetworksettings-Small.jpg" /></div><div class="imageElement"><h3>04-Select Hostname_DNS Tab Uncheck Update DNS data Via DHCP and Enter External
Nameserver
</h3><p></p><a class="open" title="open image" href="#"></a><img class="full" src="http://www.garchibald.com/images/MoonlightInstall/02-NetworkSetup%20%28Optional%29/04-SelectHostname_DNSTabUncheckUpdateDNSdataViaDHCPandEnterExternalNameserver.jpg" /><img class="thumbnail" src="http://www.garchibald.com/images/MoonlightInstall/02-NetworkSetup%20%28Optional%29/04-SelectHostname_DNSTabUncheckUpdateDNSdataViaDHCPandEnterExternalNameserver-Small.jpg" /></div><div class="imageElement"><h3>05-Click Finish
</h3><p></p><a class="open" title="open image" href="#"></a><img class="full" src="http://www.garchibald.com/images/MoonlightInstall/02-NetworkSetup%20%28Optional%29/05-ClickFinish.jpg" /><img class="thumbnail" src="http://www.garchibald.com/images/MoonlightInstall/02-NetworkSetup%20%28Optional%29/05-ClickFinish-Small.jpg" /></div><div class="imageElement"><h3>06-Configuration
</h3><p></p><a class="open" title="open image" href="#"></a><img class="full" src="http://www.garchibald.com/images/MoonlightInstall/02-NetworkSetup%20%28Optional%29/06-Configuration.jpg" /><img class="thumbnail" src="http://www.garchibald.com/images/MoonlightInstall/02-NetworkSetup%20%28Optional%29/06-Configuration-Small.jpg" /></div></div><div id="gallery03Update" class="galleryElement"><h2>03-Update
</h2><div class="imageElement"><h3>01-Review and Accept Updates
</h3><p></p><a class="open" title="open image" href="#"></a><img class="full" src="http://www.garchibald.com/images/MoonlightInstall/03-Update/01-ReviewandAcceptUpdates.jpg" /><img class="thumbnail" src="http://www.garchibald.com/images/MoonlightInstall/03-Update/01-ReviewandAcceptUpdates-Small.jpg" /></div><div class="imageElement"><h3>02-Click Continue To Accept Changes
</h3><p></p><a class="open" title="open image" href="#"></a><img class="full" src="http://www.garchibald.com/images/MoonlightInstall/03-Update/02-ClickContinueToAcceptChanges.jpg" /><img class="thumbnail" src="http://www.garchibald.com/images/MoonlightInstall/03-Update/02-ClickContinueToAcceptChanges-Small.jpg" /></div><div class="imageElement"><h3>03-Online Update
</h3><p></p><a class="open" title="open image" href="#"></a><img class="full" src="http://www.garchibald.com/images/MoonlightInstall/03-Update/03-OnlineUpdate.jpg" /><img class="thumbnail" src="http://www.garchibald.com/images/MoonlightInstall/03-Update/03-OnlineUpdate-Small.jpg" /></div><div class="imageElement"><h3>04-Post Install Firefox3 Accept Agreement
</h3><p></p><a class="open" title="open image" href="#"></a><img class="full" src="http://www.garchibald.com/images/MoonlightInstall/03-Update/04-PostInstalFirefox3AcceptAgreement.jpg" /><img class="thumbnail" src="http://www.garchibald.com/images/MoonlightInstall/03-Update/04-PostInstalFirefox3AcceptAgreement-Small.jpg" /></div></div><div id="gallery04InstallMoonlight" class="galleryElement"><h2>04-Install Moonlight
</h2><div class="imageElement"><h3>01-Open Firefox-Navigate To Packman For Libmoon
</h3><p></p><a class="open" title="open image" href="#"></a><img class="full" src="http://www.garchibald.com/images/MoonlightInstall/04-Install%20Moonlight/01-OpenFirefox-NavigateToPackmanForLibmoon.jpg" /><img class="thumbnail" src="http://www.garchibald.com/images/MoonlightInstall/04-Install%20Moonlight/01-OpenFirefox-NavigateToPackmanForLibmoon-Small.jpg" /></div><div class="imageElement"><h3>02-Click OK To Open With YAST
</h3><p></p><a class="open" title="open image" href="#"></a><img class="full" src="http://www.garchibald.com/images/MoonlightInstall/04-Install%20Moonlight/02-ClickOKToOpenWithYast.jpg" /><img class="thumbnail" src="http://www.garchibald.com/images/MoonlightInstall/04-Install%20Moonlight/02-ClickOKToOpenWithYast-Small.jpg" /></div><div class="imageElement"><h3>03-Click Next To Accept Changes
</h3><p></p><a class="open" title="open image" href="#"></a><img class="full" src="http://www.garchibald.com/images/MoonlightInstall/04-Install%20Moonlight/03-ClickNextToAcceptChanges.jpg" /><img class="thumbnail" src="http://www.garchibald.com/images/MoonlightInstall/04-Install%20Moonlight/03-ClickNextToAcceptChanges-Small.jpg" /></div><div class="imageElement"><h3>04-Click Next To Accept Install
</h3><p></p><a class="open" title="open image" href="#"></a><img class="full" src="http://www.garchibald.com/images/MoonlightInstall/04-Install%20Moonlight/04-ClickNextToAcceptInstall.jpg" /><img class="thumbnail" src="http://www.garchibald.com/images/MoonlightInstall/04-Install%20Moonlight/04-ClickNextToAcceptInstall-Small.jpg" /></div><div class="imageElement"><h3>05-Click Yes To Accept Changes
</h3><p></p><a class="open" title="open image" href="#"></a><img class="full" src="http://www.garchibald.com/images/MoonlightInstall/04-Install%20Moonlight/05-ClickYesToAcceptChanges.jpg" /><img class="thumbnail" src="http://www.garchibald.com/images/MoonlightInstall/04-Install%20Moonlight/05-ClickYesToAcceptChanges-Small.jpg" /></div><div class="imageElement"><h3>06-Enter Admin Password To Allow Packages To Be Installed
</h3><p></p><a class="open" title="open image" href="#"></a><img class="full" src="http://www.garchibald.com/images/MoonlightInstall/04-Install%20Moonlight/06-EnterAdminPasswordToAllowPackagesToBeInstalled.jpg" /><img class="thumbnail" src="http://www.garchibald.com/images/MoonlightInstall/04-Install%20Moonlight/06-EnterAdminPasswordToAllowPackagesToBeInstalled-Small.jpg" /></div><div class="imageElement"><h3>07-Import The Public Key For Pack Man
</h3><p></p><a class="open" title="open image" href="#"></a><img class="full" src="http://www.garchibald.com/images/MoonlightInstall/04-Install%20Moonlight/07-ImportThePublicKeyForPackMan.jpg" /><img class="thumbnail" src="http://www.garchibald.com/images/MoonlightInstall/04-Install%20Moonlight/07-ImportThePublicKeyForPackMan-Small.jpg" /></div><div class="imageElement"><h3>08-Click Finish
</h3><p></p><a class="open" title="open image" href="#"></a><img class="full" src="http://www.garchibald.com/images/MoonlightInstall/04-Install%20Moonlight/08-ClickFinish.jpg" /><img class="thumbnail" src="http://www.garchibald.com/images/MoonlightInstall/04-Install%20Moonlight/08-ClickFinish-Small.jpg" /></div><div class="imageElement"><h3>09-Close And Reopen The Browser
</h3><p></p><a class="open" title="open image" href="#"></a><img class="full" src="http://www.garchibald.com/images/MoonlightInstall/04-Install%20Moonlight/09-CloseAndReopenTheBrowser.jpg" /><img class="thumbnail" src="http://www.garchibald.com/images/MoonlightInstall/04-Install%20Moonlight/09-CloseAndReopenTheBrowser-Small.jpg" /></div></div><div id="gallery05Fonts" class="galleryElement"><h2>05-Fonts
</h2><div class="imageElement"><h3>01-Open Fonts Page
</h3><p></p><a class="open" title="open image" href="#"></a><img class="full" src="http://www.garchibald.com/images/MoonlightInstall/05-Fonts/01-OpenFontsPage.jpg" /><img class="thumbnail" src="http://www.garchibald.com/images/MoonlightInstall/05-Fonts/01-OpenFontsPage-Small.jpg" /></div><div class="imageElement"><h3>02-Save Fonts Rpm
</h3><p></p><a class="open" title="open image" href="#"></a><img class="full" src="http://www.garchibald.com/images/MoonlightInstall/05-Fonts/02-SaveFontsRpm.jpg" /><img class="thumbnail" src="http://www.garchibald.com/images/MoonlightInstall/05-Fonts/02-SaveFontsRpm-Small.jpg" /></div><div class="imageElement"><h3>03-Close Dialog And Browser
</h3><p></p><a class="open" title="open image" href="#"></a><img class="full" src="http://www.garchibald.com/images/MoonlightInstall/05-Fonts/03-CloseDialogAndBrowser.jpg" /><img class="thumbnail" src="http://www.garchibald.com/images/MoonlightInstall/05-Fonts/03-CloseDialogAndBrowser-Small.jpg" /></div><div class="imageElement"><h3>04-Enter The Admin Password
</h3><p></p><a class="open" title="open image" href="#"></a><img class="full" src="http://www.garchibald.com/images/MoonlightInstall/05-Fonts/04-EnterTheAdminPassword.jpg" /><img class="thumbnail" src="http://www.garchibald.com/images/MoonlightInstall/05-Fonts/04-EnterTheAdminPassword-Small.jpg" /></div></div><div id="gallery06Sound" class="galleryElement"><h2>06-Sound
</h2><div class="imageElement"><h3>01-Open YAST Hardware And Select Sound
</h3><p></p><a class="open" title="open image" href="#"></a><img class="full" src="http://www.garchibald.com/images/MoonlightInstall/06-Sound/01-OpenYsatHardwareAndSelectSound.jpg" /><img class="thumbnail" src="http://www.garchibald.com/images/MoonlightInstall/06-Sound/01-OpenYsatHardwareAndSelectSound-Small.jpg" /></div><div class="imageElement"><h3>02-Scan For Old Hardware
</h3><p></p><a class="open" title="open image" href="#"></a><img class="full" src="http://www.garchibald.com/images/MoonlightInstall/06-Sound/02-ScanForOldHardware.jpg" /><img class="thumbnail" src="http://www.garchibald.com/images/MoonlightInstall/06-Sound/02-ScanForOldHardware-Small.jpg" /></div><div class="imageElement"><h3>03-Edit
</h3><p></p><a class="open" title="open image" href="#"></a><img class="full" src="http://www.garchibald.com/images/MoonlightInstall/06-Sound/03-Edit.jpg" /><img class="thumbnail" src="http://www.garchibald.com/images/MoonlightInstall/06-Sound/03-Edit-Small.jpg" /></div><div class="imageElement"><h3>04-Add Card- Select Creative Labs SB16
</h3><p></p><a class="open" title="open image" href="#"></a><img class="full" src="http://www.garchibald.com/images/MoonlightInstall/06-Sound/04-AddCard-SelectCreateiveLabsSB16.jpg" /><img class="thumbnail" src="http://www.garchibald.com/images/MoonlightInstall/06-Sound/04-AddCard-SelectCreateiveLabsSB16-Small.jpg" /></div><div class="imageElement"><h3>05-Advanced Options
</h3><p></p><a class="open" title="open image" href="#"></a><img class="full" src="http://www.garchibald.com/images/MoonlightInstall/06-Sound/05-AdvancedOptions.jpg" /><img class="thumbnail" src="http://www.garchibald.com/images/MoonlightInstall/06-Sound/05-AdvancedOptions-Small.jpg" /></div><div class="imageElement"><h3>06-Select Advanced Options
</h3><p></p><a class="open" title="open image" href="#"></a><img class="full" src="http://www.garchibald.com/images/MoonlightInstall/06-Sound/06-SelectAdvancedOptions.jpg" /><img class="thumbnail" src="http://www.garchibald.com/images/MoonlightInstall/06-Sound/06-SelectAdvancedOptions-Small.jpg" /></div><div class="imageElement"><h3>06-Test The Sound
</h3><p></p><a class="open" title="open image" href="#"></a><img class="full" src="http://www.garchibald.com/images/MoonlightInstall/06-Sound/06-TestTheSound.jpg" /><img class="thumbnail" src="http://www.garchibald.com/images/MoonlightInstall/06-Sound/06-TestTheSound-Small.jpg" /></div><div class="imageElement"><h3>07-Finish
</h3><p></p><a class="open" title="open image" href="#"></a><img class="full" src="http://www.garchibald.com/images/MoonlightInstall/06-Sound/07-Finish.jpg" /><img class="thumbnail" src="http://www.garchibald.com/images/MoonlightInstall/06-Sound/07-Finish-Small.jpg" /></div><div class="imageElement"><h3>08-Will Save Sound Changes
</h3><p></p><a class="open" title="open image" href="#"></a><img class="full" src="http://www.garchibald.com/images/MoonlightInstall/06-Sound/08-WillSaveSoundChanges.jpg" /><img class="thumbnail" src="http://www.garchibald.com/images/MoonlightInstall/06-Sound/08-WillSaveSoundChanges-Small.jpg" /></div><div class="imageElement"><h3>09-Install alsatools
</h3><p></p><a class="open" title="open image" href="#"></a><img class="full" src="http://www.garchibald.com/images/MoonlightInstall/06-Sound/09-Installalsatools.jpg" /><img class="thumbnail" src="http://www.garchibald.com/images/MoonlightInstall/06-Sound/09-Installalsatools-Small.jpg" /></div></div><div id="gallery07Troubleshooting" class="galleryElement"><h2>07-Troubleshooting
</h2><div class="imageElement"><h3>Boot Options
</h3><p></p><a class="open" title="open image" href="#"></a><img class="full" src="http://www.garchibald.com/images/MoonlightInstall/07-Troubleshooting/BootOptions.jpg" /><img class="thumbnail" src="http://www.garchibald.com/images/MoonlightInstall/07-Troubleshooting/BootOptions-Small.jpg" /></div></div></div></div><img width="0" height="0" src="http://www.garchibald.com/blog/aggbug.ashx?id=8f0a0a19-322d-461b-86ac-2de8ca4a1df3" /></body>
      <title>Installing Moonlight within Microsoft Virtual PC 2007 using openSUSE</title>
      <guid isPermaLink="false">http://www.garchibald.com/blog/PermaLink,guid,8f0a0a19-322d-461b-86ac-2de8ca4a1df3.aspx</guid>
      <link>http://www.garchibald.com/blog/2008/07/09/InstallingMoonlightWithinMicrosoftVirtualPC2007UsingOpenSUSE.aspx</link>
      <pubDate>Wed, 09 Jul 2008 13:32:51 GMT</pubDate>
      <description>&lt;p&gt;
Wanting to dip your toe in an see the current state of Moonlight under Linux or check
the compatibility of your Silverlight 1.0 application with Moonlight then read on.&lt;br&gt;
&lt;br&gt;
Until recently to install Moonlight you had two choices: 
&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
Use the non multimedia version from the official &lt;a href="http://www.go-mono.com/moonlight/"&gt;Moonlight
Download&lt;/a&gt; page.&lt;/li&gt;
&lt;li&gt;
&lt;a href="http://www.mono-project.com/Moonlight"&gt;Compile from the source files&lt;/a&gt; for
full Moonlight multimedia support.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;
Fortunately there is a precompiled package on the Third Party openSUSE site that allows
a relatively easy install process for openSUSE. In this post I am going to walk through
the process of setting up the current &lt;a href="http://www.mono-project.com/news/archive/2008/Jul-02.html"&gt;Moonlight
Build 0.7&lt;/a&gt; that is available as a 1 Click install within OpenSUSE 10.2 or newer
using Microsoft Virtual PC 2007.
&lt;/p&gt;
&lt;p&gt;
&lt;/p&gt;
The main steps in the process are:&lt;br&gt;
&lt;ul&gt;
&lt;li&gt;
Download &lt;a href="http://en.opensuse.org/Download"&gt;openSUSE LiveCD Version 11&lt;/a&gt; from
the download site. For this post I have used the KDE version. 
&lt;/li&gt;
&lt;li&gt;
Install of the Live CD onto the Virtual Hard Disk. 
&lt;/li&gt;
&lt;li&gt;
Optional update of network settings for DNS server. This is only required if you cannot
access the Internet by default from within the virtual client. 
&lt;/li&gt;
&lt;li&gt;
Online update to get security updates and latest Version 3.0 Firefox browser. 
&lt;/li&gt;
&lt;li&gt;
Install of &lt;a href="http://packman.links2linux.org/package/libmoon"&gt;Moonlight from
PackMan&lt;/a&gt; using 1-Click install. 
&lt;/li&gt;
&lt;li&gt;
Install of &lt;a href="http://en.opensuse.org/Optimal_Use_of_MS_TrueType_Core_Fonts_for_a_KDE_Desktop_on_SuSE"&gt;Microsoft
TrueType fonts&lt;/a&gt; to ensure correct rendering in the browser.&lt;br&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
&lt;/p&gt;
&lt;script type="text/javascript"&gt;
	window.addEvent('domready', function() {
		document.moonlightInstallGallerySet = new gallerySet($('moonlightInstallGallerySet'), {
		timed: false,
		embedLinks: false
		});
	});
	&lt;/script&gt;
&lt;div id="content"&gt;
&lt;div id="moonlightInstallGallerySet" class="galleryContainer"&gt;
&lt;div id="gallery01VPCInstall" class="galleryElement"&gt;
&lt;h2&gt;01-VPC Install
&lt;/h2&gt;
&lt;div class="imageElement"&gt;
&lt;h3&gt;01-Virtual PC-Setup New
&lt;/h3&gt;
&lt;p&gt;
&lt;/p&gt;
&lt;a class="open" title="open image" href="#"&gt;&lt;/a&gt;&lt;img class="full" src="http://www.garchibald.com/images/MoonlightInstall/01-VPCInstall/01-VirtualPC-SetupNew.jpg"&gt; &lt;img class="thumbnail" src="http://www.garchibald.com/images/MoonlightInstall/01-VPCInstall/01-VirtualPC-SetupNew-Small.jpg"&gt; 
&lt;/div&gt;
&lt;div class="imageElement"&gt;
&lt;h3&gt;02-Create A Virtual Machine
&lt;/h3&gt;
&lt;p&gt;
&lt;/p&gt;
&lt;a class="open" title="open image" href="#"&gt;&lt;/a&gt;&lt;img class="full" src="http://www.garchibald.com/images/MoonlightInstall/01-VPCInstall/02-CreateAVirtualMachine.jpg"&gt; &lt;img class="thumbnail" src="http://www.garchibald.com/images/MoonlightInstall/01-VPCInstall/02-CreateAVirtualMachine-Small.jpg"&gt; 
&lt;/div&gt;
&lt;div class="imageElement"&gt;
&lt;h3&gt;03-Name The Machine
&lt;/h3&gt;
&lt;p&gt;
&lt;/p&gt;
&lt;a class="open" title="open image" href="#"&gt;&lt;/a&gt;&lt;img class="full" src="http://www.garchibald.com/images/MoonlightInstall/01-VPCInstall/03-NameTheMachine.jpg"&gt; &lt;img class="thumbnail" src="http://www.garchibald.com/images/MoonlightInstall/01-VPCInstall/03-NameTheMachine-Small.jpg"&gt; 
&lt;/div&gt;
&lt;div class="imageElement"&gt;
&lt;h3&gt;04-Select Other Operating System
&lt;/h3&gt;
&lt;p&gt;
&lt;/p&gt;
&lt;a class="open" title="open image" href="#"&gt;&lt;/a&gt;&lt;img class="full" src="http://www.garchibald.com/images/MoonlightInstall/01-VPCInstall/04-SelectOtherOperatingSystem.jpg"&gt; &lt;img class="thumbnail" src="http://www.garchibald.com/images/MoonlightInstall/01-VPCInstall/04-SelectOtherOperatingSystem-Small.jpg"&gt; 
&lt;/div&gt;
&lt;div class="imageElement"&gt;
&lt;h3&gt;05-Finish To Complete
&lt;/h3&gt;
&lt;p&gt;
&lt;/p&gt;
&lt;a class="open" title="open image" href="#"&gt;&lt;/a&gt;&lt;img class="full" src="http://www.garchibald.com/images/MoonlightInstall/01-VPCInstall/05-FinishToComplete.jpg"&gt; &lt;img class="thumbnail" src="http://www.garchibald.com/images/MoonlightInstall/01-VPCInstall/05-FinishToComplete-Small.jpg"&gt; 
&lt;/div&gt;
&lt;div class="imageElement"&gt;
&lt;h3&gt;06-Start The Virtual Machine
&lt;/h3&gt;
&lt;p&gt;
&lt;/p&gt;
&lt;a class="open" title="open image" href="#"&gt;&lt;/a&gt;&lt;img class="full" src="http://www.garchibald.com/images/MoonlightInstall/01-VPCInstall/06-StartTheVirtualMachine.jpg"&gt; &lt;img class="thumbnail" src="http://www.garchibald.com/images/MoonlightInstall/01-VPCInstall/06-StartTheVirtualMachine-Small.jpg"&gt; 
&lt;/div&gt;
&lt;div class="imageElement"&gt;
&lt;h3&gt;07-Capture The Live Cd Iso
&lt;/h3&gt;
&lt;p&gt;
&lt;/p&gt;
&lt;a class="open" title="open image" href="#"&gt;&lt;/a&gt;&lt;img class="full" src="http://www.garchibald.com/images/MoonlightInstall/01-VPCInstall/07-CaptureTheLiveCdIso.jpg"&gt; &lt;img class="thumbnail" src="http://www.garchibald.com/images/MoonlightInstall/01-VPCInstall/07-CaptureTheLiveCdIso-Small.jpg"&gt; 
&lt;/div&gt;
&lt;div class="imageElement"&gt;
&lt;h3&gt;08-Start Open SUSE 11
&lt;/h3&gt;
&lt;p&gt;
&lt;/p&gt;
&lt;a class="open" title="open image" href="#"&gt;&lt;/a&gt;&lt;img class="full" src="http://www.garchibald.com/images/MoonlightInstall/01-VPCInstall/08-StartOpenSUSE11.jpg"&gt; &lt;img class="thumbnail" src="http://www.garchibald.com/images/MoonlightInstall/01-VPCInstall/08-StartOpenSUSE11-Small.jpg"&gt; 
&lt;/div&gt;
&lt;div class="imageElement"&gt;
&lt;h3&gt;09-Close Welcome Dialog
&lt;/h3&gt;
&lt;p&gt;
&lt;/p&gt;
&lt;a class="open" title="open image" href="#"&gt;&lt;/a&gt;&lt;img class="full" src="http://www.garchibald.com/images/MoonlightInstall/01-VPCInstall/09-CloseWelcomeDialog.jpg"&gt; &lt;img class="thumbnail" src="http://www.garchibald.com/images/MoonlightInstall/01-VPCInstall/09-CloseWelcomeDialog-Small.jpg"&gt; 
&lt;/div&gt;
&lt;div class="imageElement"&gt;
&lt;h3&gt;10-Select Install From Desktop
&lt;/h3&gt;
&lt;p&gt;
&lt;/p&gt;
&lt;a class="open" title="open image" href="#"&gt;&lt;/a&gt;&lt;img class="full" src="http://www.garchibald.com/images/MoonlightInstall/01-VPCInstall/10-SelectInstallFromDesktop.jpg"&gt; &lt;img class="thumbnail" src="http://www.garchibald.com/images/MoonlightInstall/01-VPCInstall/10-SelectInstallFromDesktop-Small.jpg"&gt; 
&lt;/div&gt;
&lt;div class="imageElement"&gt;
&lt;h3&gt;11-Agree To License Terms And Click Next
&lt;/h3&gt;
&lt;p&gt;
&lt;/p&gt;
&lt;a class="open" title="open image" href="#"&gt;&lt;/a&gt;&lt;img class="full" src="http://www.garchibald.com/images/MoonlightInstall/01-VPCInstall/11-AgreeToLicenseTermsAndClickNext.jpg"&gt; &lt;img class="thumbnail" src="http://www.garchibald.com/images/MoonlightInstall/01-VPCInstall/11-AgreeToLicenseTermsAndClickNext-Small.jpg"&gt; 
&lt;/div&gt;
&lt;div class="imageElement"&gt;
&lt;h3&gt;12-Select Timezone And Next
&lt;/h3&gt;
&lt;p&gt;
&lt;/p&gt;
&lt;a class="open" title="open image" href="#"&gt;&lt;/a&gt;&lt;img class="full" src="http://www.garchibald.com/images/MoonlightInstall/01-VPCInstall/12-SelectTimezoneAndNext.jpg"&gt; &lt;img class="thumbnail" src="http://www.garchibald.com/images/MoonlightInstall/01-VPCInstall/12-SelectTimezoneAndNext-Small.jpg"&gt; 
&lt;/div&gt;
&lt;div class="imageElement"&gt;
&lt;h3&gt;13-Accept Default Partitions
&lt;/h3&gt;
&lt;p&gt;
&lt;/p&gt;
&lt;a class="open" title="open image" href="#"&gt;&lt;/a&gt;&lt;img class="full" src="http://www.garchibald.com/images/MoonlightInstall/01-VPCInstall/13-AcceptDefaultPartitions.jpg"&gt; &lt;img class="thumbnail" src="http://www.garchibald.com/images/MoonlightInstall/01-VPCInstall/13-AcceptDefaultPartitions-Small.jpg"&gt; 
&lt;/div&gt;
&lt;div class="imageElement"&gt;
&lt;h3&gt;14-Enter User Name And Password
&lt;/h3&gt;
&lt;p&gt;
&lt;/p&gt;
&lt;a class="open" title="open image" href="#"&gt;&lt;/a&gt;&lt;img class="full" src="http://www.garchibald.com/images/MoonlightInstall/01-VPCInstall/14-EnterUserNameAndPassword.jpg"&gt; &lt;img class="thumbnail" src="http://www.garchibald.com/images/MoonlightInstall/01-VPCInstall/14-EnterUserNameAndPassword-Small.jpg"&gt; 
&lt;/div&gt;
&lt;div class="imageElement"&gt;
&lt;h3&gt;15-Review Install And Click Install
&lt;/h3&gt;
&lt;p&gt;
&lt;/p&gt;
&lt;a class="open" title="open image" href="#"&gt;&lt;/a&gt;&lt;img class="full" src="http://www.garchibald.com/images/MoonlightInstall/01-VPCInstall/15-ReviewInstallAndClickInstall.jpg"&gt; &lt;img class="thumbnail" src="http://www.garchibald.com/images/MoonlightInstall/01-VPCInstall/15-ReviewInstallAndClickInstall-Small.jpg"&gt; 
&lt;/div&gt;
&lt;div class="imageElement"&gt;
&lt;h3&gt;16-Click Install To Confirm
&lt;/h3&gt;
&lt;p&gt;
&lt;/p&gt;
&lt;a class="open" title="open image" href="#"&gt;&lt;/a&gt;&lt;img class="full" src="http://www.garchibald.com/images/MoonlightInstall/01-VPCInstall/16-ClickInstallToConfirm.jpg"&gt; &lt;img class="thumbnail" src="http://www.garchibald.com/images/MoonlightInstall/01-VPCInstall/16-ClickInstallToConfirm-Small.jpg"&gt; 
&lt;/div&gt;
&lt;div class="imageElement"&gt;
&lt;h3&gt;17-Click OK And Release Iso
&lt;/h3&gt;
&lt;p&gt;
&lt;/p&gt;
&lt;a class="open" title="open image" href="#"&gt;&lt;/a&gt;&lt;img class="full" src="http://www.garchibald.com/images/MoonlightInstall/01-VPCInstall/17-ClickOKAndReleaseIso.jpg"&gt; &lt;img class="thumbnail" src="http://www.garchibald.com/images/MoonlightInstall/01-VPCInstall/17-ClickOKAndReleaseIso-Small.jpg"&gt; 
&lt;/div&gt;
&lt;div class="imageElement"&gt;
&lt;h3&gt;18-Next To Reboot
&lt;/h3&gt;
&lt;p&gt;
&lt;/p&gt;
&lt;a class="open" title="open image" href="#"&gt;&lt;/a&gt;&lt;img class="full" src="http://www.garchibald.com/images/MoonlightInstall/01-VPCInstall/18-NextToReboot.jpg"&gt; &lt;img class="thumbnail" src="http://www.garchibald.com/images/MoonlightInstall/01-VPCInstall/18-NextToReboot-Small.jpg"&gt; 
&lt;/div&gt;
&lt;div class="imageElement"&gt;
&lt;h3&gt;19-Will Startup For First Time Time
&lt;/h3&gt;
&lt;p&gt;
&lt;/p&gt;
&lt;a class="open" title="open image" href="#"&gt;&lt;/a&gt;&lt;img class="full" src="http://www.garchibald.com/images/MoonlightInstall/01-VPCInstall/19-WillStartupForFirstTimeTime.jpg"&gt; &lt;img class="thumbnail" src="http://www.garchibald.com/images/MoonlightInstall/01-VPCInstall/19-WillStartupForFirstTimeTime-Small.jpg"&gt; 
&lt;/div&gt;
&lt;div class="imageElement"&gt;
&lt;h3&gt;20-Close Dialog
&lt;/h3&gt;
&lt;p&gt;
&lt;/p&gt;
&lt;a class="open" title="open image" href="#"&gt;&lt;/a&gt;&lt;img class="full" src="http://www.garchibald.com/images/MoonlightInstall/01-VPCInstall/20-CloseDialog.jpg"&gt; &lt;img class="thumbnail" src="http://www.garchibald.com/images/MoonlightInstall/01-VPCInstall/20-CloseDialog-Small.jpg"&gt; 
&lt;/div&gt;
&lt;/div&gt;
&lt;div id="gallery02NetworkSetup(Optional)" class="galleryElement"&gt;
&lt;h2&gt;02-Network Setup (Optional)
&lt;/h2&gt;
&lt;div class="imageElement"&gt;
&lt;h3&gt;01-Select YAST To Administer Settings
&lt;/h3&gt;
&lt;p&gt;
&lt;/p&gt;
&lt;a class="open" title="open image" href="#"&gt;&lt;/a&gt;&lt;img class="full" src="http://www.garchibald.com/images/MoonlightInstall/02-NetworkSetup%20%28Optional%29/01-SelectYASTToAdministerSettings.jpg"&gt; &lt;img class="thumbnail" src="http://www.garchibald.com/images/MoonlightInstall/02-NetworkSetup%20%28Optional%29/01-SelectYASTToAdministerSettings-Small.jpg"&gt; 
&lt;/div&gt;
&lt;div class="imageElement"&gt;
&lt;h3&gt;02-Enter Password Setup Earlier For Admin User
&lt;/h3&gt;
&lt;p&gt;
&lt;/p&gt;
&lt;a class="open" title="open image" href="#"&gt;&lt;/a&gt;&lt;img class="full" src="http://www.garchibald.com/images/MoonlightInstall/02-NetworkSetup%20%28Optional%29/02-EnterPasswordSetupEarlierForAdminUser.jpg"&gt; &lt;img class="thumbnail" src="http://www.garchibald.com/images/MoonlightInstall/02-NetworkSetup%20%28Optional%29/02-EnterPasswordSetupEarlierForAdminUser-Small.jpg"&gt; 
&lt;/div&gt;
&lt;div class="imageElement"&gt;
&lt;h3&gt;03-Select Network Devices And Click On Network settings
&lt;/h3&gt;
&lt;p&gt;
&lt;/p&gt;
&lt;a class="open" title="open image" href="#"&gt;&lt;/a&gt;&lt;img class="full" src="http://www.garchibald.com/images/MoonlightInstall/02-NetworkSetup%20%28Optional%29/03-SelectNetworkDevicesAndClickOnNetworksettings.jpg"&gt; &lt;img class="thumbnail" src="http://www.garchibald.com/images/MoonlightInstall/02-NetworkSetup%20%28Optional%29/03-SelectNetworkDevicesAndClickOnNetworksettings-Small.jpg"&gt; 
&lt;/div&gt;
&lt;div class="imageElement"&gt;
&lt;h3&gt;04-Select Hostname_DNS Tab Uncheck Update DNS data Via DHCP and Enter External
Nameserver
&lt;/h3&gt;
&lt;p&gt;
&lt;/p&gt;
&lt;a class="open" title="open image" href="#"&gt;&lt;/a&gt;&lt;img class="full" src="http://www.garchibald.com/images/MoonlightInstall/02-NetworkSetup%20%28Optional%29/04-SelectHostname_DNSTabUncheckUpdateDNSdataViaDHCPandEnterExternalNameserver.jpg"&gt; &lt;img class="thumbnail" src="http://www.garchibald.com/images/MoonlightInstall/02-NetworkSetup%20%28Optional%29/04-SelectHostname_DNSTabUncheckUpdateDNSdataViaDHCPandEnterExternalNameserver-Small.jpg"&gt; 
&lt;/div&gt;
&lt;div class="imageElement"&gt;
&lt;h3&gt;05-Click Finish
&lt;/h3&gt;
&lt;p&gt;
&lt;/p&gt;
&lt;a class="open" title="open image" href="#"&gt;&lt;/a&gt;&lt;img class="full" src="http://www.garchibald.com/images/MoonlightInstall/02-NetworkSetup%20%28Optional%29/05-ClickFinish.jpg"&gt; &lt;img class="thumbnail" src="http://www.garchibald.com/images/MoonlightInstall/02-NetworkSetup%20%28Optional%29/05-ClickFinish-Small.jpg"&gt; 
&lt;/div&gt;
&lt;div class="imageElement"&gt;
&lt;h3&gt;06-Configuration
&lt;/h3&gt;
&lt;p&gt;
&lt;/p&gt;
&lt;a class="open" title="open image" href="#"&gt;&lt;/a&gt;&lt;img class="full" src="http://www.garchibald.com/images/MoonlightInstall/02-NetworkSetup%20%28Optional%29/06-Configuration.jpg"&gt; &lt;img class="thumbnail" src="http://www.garchibald.com/images/MoonlightInstall/02-NetworkSetup%20%28Optional%29/06-Configuration-Small.jpg"&gt; 
&lt;/div&gt;
&lt;/div&gt;
&lt;div id="gallery03Update" class="galleryElement"&gt;
&lt;h2&gt;03-Update
&lt;/h2&gt;
&lt;div class="imageElement"&gt;
&lt;h3&gt;01-Review and Accept Updates
&lt;/h3&gt;
&lt;p&gt;
&lt;/p&gt;
&lt;a class="open" title="open image" href="#"&gt;&lt;/a&gt;&lt;img class="full" src="http://www.garchibald.com/images/MoonlightInstall/03-Update/01-ReviewandAcceptUpdates.jpg"&gt; &lt;img class="thumbnail" src="http://www.garchibald.com/images/MoonlightInstall/03-Update/01-ReviewandAcceptUpdates-Small.jpg"&gt; 
&lt;/div&gt;
&lt;div class="imageElement"&gt;
&lt;h3&gt;02-Click Continue To Accept Changes
&lt;/h3&gt;
&lt;p&gt;
&lt;/p&gt;
&lt;a class="open" title="open image" href="#"&gt;&lt;/a&gt;&lt;img class="full" src="http://www.garchibald.com/images/MoonlightInstall/03-Update/02-ClickContinueToAcceptChanges.jpg"&gt; &lt;img class="thumbnail" src="http://www.garchibald.com/images/MoonlightInstall/03-Update/02-ClickContinueToAcceptChanges-Small.jpg"&gt; 
&lt;/div&gt;
&lt;div class="imageElement"&gt;
&lt;h3&gt;03-Online Update
&lt;/h3&gt;
&lt;p&gt;
&lt;/p&gt;
&lt;a class="open" title="open image" href="#"&gt;&lt;/a&gt;&lt;img class="full" src="http://www.garchibald.com/images/MoonlightInstall/03-Update/03-OnlineUpdate.jpg"&gt; &lt;img class="thumbnail" src="http://www.garchibald.com/images/MoonlightInstall/03-Update/03-OnlineUpdate-Small.jpg"&gt; 
&lt;/div&gt;
&lt;div class="imageElement"&gt;
&lt;h3&gt;04-Post Install Firefox3 Accept Agreement
&lt;/h3&gt;
&lt;p&gt;
&lt;/p&gt;
&lt;a class="open" title="open image" href="#"&gt;&lt;/a&gt;&lt;img class="full" src="http://www.garchibald.com/images/MoonlightInstall/03-Update/04-PostInstalFirefox3AcceptAgreement.jpg"&gt; &lt;img class="thumbnail" src="http://www.garchibald.com/images/MoonlightInstall/03-Update/04-PostInstalFirefox3AcceptAgreement-Small.jpg"&gt; 
&lt;/div&gt;
&lt;/div&gt;
&lt;div id="gallery04InstallMoonlight" class="galleryElement"&gt;
&lt;h2&gt;04-Install Moonlight
&lt;/h2&gt;
&lt;div class="imageElement"&gt;
&lt;h3&gt;01-Open Firefox-Navigate To Packman For Libmoon
&lt;/h3&gt;
&lt;p&gt;
&lt;/p&gt;
&lt;a class="open" title="open image" href="#"&gt;&lt;/a&gt;&lt;img class="full" src="http://www.garchibald.com/images/MoonlightInstall/04-Install%20Moonlight/01-OpenFirefox-NavigateToPackmanForLibmoon.jpg"&gt; &lt;img class="thumbnail" src="http://www.garchibald.com/images/MoonlightInstall/04-Install%20Moonlight/01-OpenFirefox-NavigateToPackmanForLibmoon-Small.jpg"&gt; 
&lt;/div&gt;
&lt;div class="imageElement"&gt;
&lt;h3&gt;02-Click OK To Open With YAST
&lt;/h3&gt;
&lt;p&gt;
&lt;/p&gt;
&lt;a class="open" title="open image" href="#"&gt;&lt;/a&gt;&lt;img class="full" src="http://www.garchibald.com/images/MoonlightInstall/04-Install%20Moonlight/02-ClickOKToOpenWithYast.jpg"&gt; &lt;img class="thumbnail" src="http://www.garchibald.com/images/MoonlightInstall/04-Install%20Moonlight/02-ClickOKToOpenWithYast-Small.jpg"&gt; 
&lt;/div&gt;
&lt;div class="imageElement"&gt;
&lt;h3&gt;03-Click Next To Accept Changes
&lt;/h3&gt;
&lt;p&gt;
&lt;/p&gt;
&lt;a class="open" title="open image" href="#"&gt;&lt;/a&gt;&lt;img class="full" src="http://www.garchibald.com/images/MoonlightInstall/04-Install%20Moonlight/03-ClickNextToAcceptChanges.jpg"&gt; &lt;img class="thumbnail" src="http://www.garchibald.com/images/MoonlightInstall/04-Install%20Moonlight/03-ClickNextToAcceptChanges-Small.jpg"&gt; 
&lt;/div&gt;
&lt;div class="imageElement"&gt;
&lt;h3&gt;04-Click Next To Accept Install
&lt;/h3&gt;
&lt;p&gt;
&lt;/p&gt;
&lt;a class="open" title="open image" href="#"&gt;&lt;/a&gt;&lt;img class="full" src="http://www.garchibald.com/images/MoonlightInstall/04-Install%20Moonlight/04-ClickNextToAcceptInstall.jpg"&gt; &lt;img class="thumbnail" src="http://www.garchibald.com/images/MoonlightInstall/04-Install%20Moonlight/04-ClickNextToAcceptInstall-Small.jpg"&gt; 
&lt;/div&gt;
&lt;div class="imageElement"&gt;
&lt;h3&gt;05-Click Yes To Accept Changes
&lt;/h3&gt;
&lt;p&gt;
&lt;/p&gt;
&lt;a class="open" title="open image" href="#"&gt;&lt;/a&gt;&lt;img class="full" src="http://www.garchibald.com/images/MoonlightInstall/04-Install%20Moonlight/05-ClickYesToAcceptChanges.jpg"&gt; &lt;img class="thumbnail" src="http://www.garchibald.com/images/MoonlightInstall/04-Install%20Moonlight/05-ClickYesToAcceptChanges-Small.jpg"&gt; 
&lt;/div&gt;
&lt;div class="imageElement"&gt;
&lt;h3&gt;06-Enter Admin Password To Allow Packages To Be Installed
&lt;/h3&gt;
&lt;p&gt;
&lt;/p&gt;
&lt;a class="open" title="open image" href="#"&gt;&lt;/a&gt;&lt;img class="full" src="http://www.garchibald.com/images/MoonlightInstall/04-Install%20Moonlight/06-EnterAdminPasswordToAllowPackagesToBeInstalled.jpg"&gt; &lt;img class="thumbnail" src="http://www.garchibald.com/images/MoonlightInstall/04-Install%20Moonlight/06-EnterAdminPasswordToAllowPackagesToBeInstalled-Small.jpg"&gt; 
&lt;/div&gt;
&lt;div class="imageElement"&gt;
&lt;h3&gt;07-Import The Public Key For Pack Man
&lt;/h3&gt;
&lt;p&gt;
&lt;/p&gt;
&lt;a class="open" title="open image" href="#"&gt;&lt;/a&gt;&lt;img class="full" src="http://www.garchibald.com/images/MoonlightInstall/04-Install%20Moonlight/07-ImportThePublicKeyForPackMan.jpg"&gt; &lt;img class="thumbnail" src="http://www.garchibald.com/images/MoonlightInstall/04-Install%20Moonlight/07-ImportThePublicKeyForPackMan-Small.jpg"&gt; 
&lt;/div&gt;
&lt;div class="imageElement"&gt;
&lt;h3&gt;08-Click Finish
&lt;/h3&gt;
&lt;p&gt;
&lt;/p&gt;
&lt;a class="open" title="open image" href="#"&gt;&lt;/a&gt;&lt;img class="full" src="http://www.garchibald.com/images/MoonlightInstall/04-Install%20Moonlight/08-ClickFinish.jpg"&gt; &lt;img class="thumbnail" src="http://www.garchibald.com/images/MoonlightInstall/04-Install%20Moonlight/08-ClickFinish-Small.jpg"&gt; 
&lt;/div&gt;
&lt;div class="imageElement"&gt;
&lt;h3&gt;09-Close And Reopen The Browser
&lt;/h3&gt;
&lt;p&gt;
&lt;/p&gt;
&lt;a class="open" title="open image" href="#"&gt;&lt;/a&gt;&lt;img class="full" src="http://www.garchibald.com/images/MoonlightInstall/04-Install%20Moonlight/09-CloseAndReopenTheBrowser.jpg"&gt; &lt;img class="thumbnail" src="http://www.garchibald.com/images/MoonlightInstall/04-Install%20Moonlight/09-CloseAndReopenTheBrowser-Small.jpg"&gt; 
&lt;/div&gt;
&lt;/div&gt;
&lt;div id="gallery05Fonts" class="galleryElement"&gt;
&lt;h2&gt;05-Fonts
&lt;/h2&gt;
&lt;div class="imageElement"&gt;
&lt;h3&gt;01-Open Fonts Page
&lt;/h3&gt;
&lt;p&gt;
&lt;/p&gt;
&lt;a class="open" title="open image" href="#"&gt;&lt;/a&gt;&lt;img class="full" src="http://www.garchibald.com/images/MoonlightInstall/05-Fonts/01-OpenFontsPage.jpg"&gt; &lt;img class="thumbnail" src="http://www.garchibald.com/images/MoonlightInstall/05-Fonts/01-OpenFontsPage-Small.jpg"&gt; 
&lt;/div&gt;
&lt;div class="imageElement"&gt;
&lt;h3&gt;02-Save Fonts Rpm
&lt;/h3&gt;
&lt;p&gt;
&lt;/p&gt;
&lt;a class="open" title="open image" href="#"&gt;&lt;/a&gt;&lt;img class="full" src="http://www.garchibald.com/images/MoonlightInstall/05-Fonts/02-SaveFontsRpm.jpg"&gt; &lt;img class="thumbnail" src="http://www.garchibald.com/images/MoonlightInstall/05-Fonts/02-SaveFontsRpm-Small.jpg"&gt; 
&lt;/div&gt;
&lt;div class="imageElement"&gt;
&lt;h3&gt;03-Close Dialog And Browser
&lt;/h3&gt;
&lt;p&gt;
&lt;/p&gt;
&lt;a class="open" title="open image" href="#"&gt;&lt;/a&gt;&lt;img class="full" src="http://www.garchibald.com/images/MoonlightInstall/05-Fonts/03-CloseDialogAndBrowser.jpg"&gt; &lt;img class="thumbnail" src="http://www.garchibald.com/images/MoonlightInstall/05-Fonts/03-CloseDialogAndBrowser-Small.jpg"&gt; 
&lt;/div&gt;
&lt;div class="imageElement"&gt;
&lt;h3&gt;04-Enter The Admin Password
&lt;/h3&gt;
&lt;p&gt;
&lt;/p&gt;
&lt;a class="open" title="open image" href="#"&gt;&lt;/a&gt;&lt;img class="full" src="http://www.garchibald.com/images/MoonlightInstall/05-Fonts/04-EnterTheAdminPassword.jpg"&gt; &lt;img class="thumbnail" src="http://www.garchibald.com/images/MoonlightInstall/05-Fonts/04-EnterTheAdminPassword-Small.jpg"&gt; 
&lt;/div&gt;
&lt;/div&gt;
&lt;div id="gallery06Sound" class="galleryElement"&gt;
&lt;h2&gt;06-Sound
&lt;/h2&gt;
&lt;div class="imageElement"&gt;
&lt;h3&gt;01-Open YAST Hardware And Select Sound
&lt;/h3&gt;
&lt;p&gt;
&lt;/p&gt;
&lt;a class="open" title="open image" href="#"&gt;&lt;/a&gt;&lt;img class="full" src="http://www.garchibald.com/images/MoonlightInstall/06-Sound/01-OpenYsatHardwareAndSelectSound.jpg"&gt; &lt;img class="thumbnail" src="http://www.garchibald.com/images/MoonlightInstall/06-Sound/01-OpenYsatHardwareAndSelectSound-Small.jpg"&gt; 
&lt;/div&gt;
&lt;div class="imageElement"&gt;
&lt;h3&gt;02-Scan For Old Hardware
&lt;/h3&gt;
&lt;p&gt;
&lt;/p&gt;
&lt;a class="open" title="open image" href="#"&gt;&lt;/a&gt;&lt;img class="full" src="http://www.garchibald.com/images/MoonlightInstall/06-Sound/02-ScanForOldHardware.jpg"&gt; &lt;img class="thumbnail" src="http://www.garchibald.com/images/MoonlightInstall/06-Sound/02-ScanForOldHardware-Small.jpg"&gt; 
&lt;/div&gt;
&lt;div class="imageElement"&gt;
&lt;h3&gt;03-Edit
&lt;/h3&gt;
&lt;p&gt;
&lt;/p&gt;
&lt;a class="open" title="open image" href="#"&gt;&lt;/a&gt;&lt;img class="full" src="http://www.garchibald.com/images/MoonlightInstall/06-Sound/03-Edit.jpg"&gt; &lt;img class="thumbnail" src="http://www.garchibald.com/images/MoonlightInstall/06-Sound/03-Edit-Small.jpg"&gt; 
&lt;/div&gt;
&lt;div class="imageElement"&gt;
&lt;h3&gt;04-Add Card- Select Creative Labs SB16
&lt;/h3&gt;
&lt;p&gt;
&lt;/p&gt;
&lt;a class="open" title="open image" href="#"&gt;&lt;/a&gt;&lt;img class="full" src="http://www.garchibald.com/images/MoonlightInstall/06-Sound/04-AddCard-SelectCreateiveLabsSB16.jpg"&gt; &lt;img class="thumbnail" src="http://www.garchibald.com/images/MoonlightInstall/06-Sound/04-AddCard-SelectCreateiveLabsSB16-Small.jpg"&gt; 
&lt;/div&gt;
&lt;div class="imageElement"&gt;
&lt;h3&gt;05-Advanced Options
&lt;/h3&gt;
&lt;p&gt;
&lt;/p&gt;
&lt;a class="open" title="open image" href="#"&gt;&lt;/a&gt;&lt;img class="full" src="http://www.garchibald.com/images/MoonlightInstall/06-Sound/05-AdvancedOptions.jpg"&gt; &lt;img class="thumbnail" src="http://www.garchibald.com/images/MoonlightInstall/06-Sound/05-AdvancedOptions-Small.jpg"&gt; 
&lt;/div&gt;
&lt;div class="imageElement"&gt;
&lt;h3&gt;06-Select Advanced Options
&lt;/h3&gt;
&lt;p&gt;
&lt;/p&gt;
&lt;a class="open" title="open image" href="#"&gt;&lt;/a&gt;&lt;img class="full" src="http://www.garchibald.com/images/MoonlightInstall/06-Sound/06-SelectAdvancedOptions.jpg"&gt; &lt;img class="thumbnail" src="http://www.garchibald.com/images/MoonlightInstall/06-Sound/06-SelectAdvancedOptions-Small.jpg"&gt; 
&lt;/div&gt;
&lt;div class="imageElement"&gt;
&lt;h3&gt;06-Test The Sound
&lt;/h3&gt;
&lt;p&gt;
&lt;/p&gt;
&lt;a class="open" title="open image" href="#"&gt;&lt;/a&gt;&lt;img class="full" src="http://www.garchibald.com/images/MoonlightInstall/06-Sound/06-TestTheSound.jpg"&gt; &lt;img class="thumbnail" src="http://www.garchibald.com/images/MoonlightInstall/06-Sound/06-TestTheSound-Small.jpg"&gt; 
&lt;/div&gt;
&lt;div class="imageElement"&gt;
&lt;h3&gt;07-Finish
&lt;/h3&gt;
&lt;p&gt;
&lt;/p&gt;
&lt;a class="open" title="open image" href="#"&gt;&lt;/a&gt;&lt;img class="full" src="http://www.garchibald.com/images/MoonlightInstall/06-Sound/07-Finish.jpg"&gt; &lt;img class="thumbnail" src="http://www.garchibald.com/images/MoonlightInstall/06-Sound/07-Finish-Small.jpg"&gt; 
&lt;/div&gt;
&lt;div class="imageElement"&gt;
&lt;h3&gt;08-Will Save Sound Changes
&lt;/h3&gt;
&lt;p&gt;
&lt;/p&gt;
&lt;a class="open" title="open image" href="#"&gt;&lt;/a&gt;&lt;img class="full" src="http://www.garchibald.com/images/MoonlightInstall/06-Sound/08-WillSaveSoundChanges.jpg"&gt; &lt;img class="thumbnail" src="http://www.garchibald.com/images/MoonlightInstall/06-Sound/08-WillSaveSoundChanges-Small.jpg"&gt; 
&lt;/div&gt;
&lt;div class="imageElement"&gt;
&lt;h3&gt;09-Install alsatools
&lt;/h3&gt;
&lt;p&gt;
&lt;/p&gt;
&lt;a class="open" title="open image" href="#"&gt;&lt;/a&gt;&lt;img class="full" src="http://www.garchibald.com/images/MoonlightInstall/06-Sound/09-Installalsatools.jpg"&gt; &lt;img class="thumbnail" src="http://www.garchibald.com/images/MoonlightInstall/06-Sound/09-Installalsatools-Small.jpg"&gt; 
&lt;/div&gt;
&lt;/div&gt;
&lt;div id="gallery07Troubleshooting" class="galleryElement"&gt;
&lt;h2&gt;07-Troubleshooting
&lt;/h2&gt;
&lt;div class="imageElement"&gt;
&lt;h3&gt;Boot Options
&lt;/h3&gt;
&lt;p&gt;
&lt;/p&gt;
&lt;a class="open" title="open image" href="#"&gt;&lt;/a&gt;&lt;img class="full" src="http://www.garchibald.com/images/MoonlightInstall/07-Troubleshooting/BootOptions.jpg"&gt; &lt;img class="thumbnail" src="http://www.garchibald.com/images/MoonlightInstall/07-Troubleshooting/BootOptions-Small.jpg"&gt; 
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;img width="0" height="0" src="http://www.garchibald.com/blog/aggbug.ashx?id=8f0a0a19-322d-461b-86ac-2de8ca4a1df3" /&gt;</description>
      <comments>http://www.garchibald.com/blog/CommentView,guid,8f0a0a19-322d-461b-86ac-2de8ca4a1df3.aspx</comments>
      <category>moonlight</category>
      <category>silverlight</category>
    </item>
  </channel>
</rss>