# Sunday, September 02, 2012

WebApi: Custom Exception Handler

While having exception information is good while debugging, for a production deployment we should remove this information as it may reveal additional information about the service. WebApi is no different.

There are many ways to catch exceptions in WebApi, but what if is the exception occurs inside a Filter?. One option is to add a Message Handler to inspect the message and remove extra information from the HttpError e.g. For a .Net 4.5 WebApi service you could use following handler

public class CustomExceptionHandler : DelegatingHandler
{
        protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
        {
            var response = await base.SendAsync(request, cancellationToken);

            if (!response.IsSuccessStatusCode && response.Content is ObjectContent<HttpError>)
            {
                var error = ((ObjectContent<HttpError>) response.Content).Value as HttpError;
                if (error != null)
                {
                    error.Remove("ExceptionType");
                    error.Remove("StackTrace");
                }
            }
            return response;
        }
  }

To register this message handler in the GlobalConfiguartion you can then add the following line

GlobalConfiguration.Configuration.MessageHandlers.Add(new CustomExceptionHandler());

# Friday, August 24, 2012

Visual Studio: Open Project Command Prompt

Do you need to open the Visual Studio Command prompt for the current project without needless keystrokes or clicks? It can be easy just do the following

Setup External Tool

  1. Open Tools –> External Tools Menu
  2. Add a new entry
  3. Add Title Open Command Prompt Here
  4. Enter Command cmd.exe
  5. Enter following in Arguments for if you are using VS 2012
    1. %comspec% /k ""C:\Program Files (x86)\Microsoft Visual Studio 11.0\Common7\Tools\VsDevCmd.bat""
  6. Enter $(ProjectDir) in Initial Directory
  7. Click Apply
  8. Then move the External Tool to the top of the list using the Move Up button

Assign Shortcut

  1. Open Tools –> Options –> Environment –> Keyboard
  2. Enter Tools.ExternalCommand1 into the text box for Show commands containing
  3. Click in Press shortcut keys and add a key sequence e.g. Ctrl+Shift+`
  4. If the keyboard shortcut is already assigned it will display the command it is already linked to
  5. If you are happy with the key combination click Apply

Now your are only a shortcut away from the command prompt should you need it.

# Monday, February 23, 2009

Three Little Pigs – Silverlight E-Book

Keeping with the MIX09 contests theme, I have submitted an entry for Adam Kinney’s Tell a Fairy tale in Silverlight or WPF and win a FREE PASS TO MIX.

The interactive Silverlight e-book version of the Three Little Pigs is available from my site on http://www.garchibald.com/ebooks/The-Three-Little-Pigs/

The e-book make use of:

  • Page flip animations to navigate the pages.
  • Text to speech to read the story.
  • JavaScript to ensure that pages can be bookmarked.

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.

# Friday, February 06, 2009

Extending the Expression Encoder 2 FTP Publishing Plug-in – Adding a WPF PasswordBox And UserControl Style

In this post I will cover the changes required to use a PasswordBox control to display the FTP password.

The following links a quick reference to the topics covered:

  • FTP Plug-in Overview – Quick description of the plug-in control created by Tim Heuer.
  • FTP Plug-in Changes – Steps to change the plug-in to use a PasswordBox control instead of TextBox.
  • Testing The Changes – Describes testing the changes with Expression Encoder and the issues encountered.
  • PasswordBox Styles – Describes to changes made to alter the style for the PasswordBox control so that it has consistent look and feel.
  • Investigating Default Styles - Investigating the Expression Encoder WPF Styles using Redgate’s .Net Reflector.
  • Final Version – Describes the final version incorporating the style changes in the previous sections.
  • Review – Describes the step taken to modify the plug-in to make use the of a PasswordBox control.

Technologies Used

  • Microsoft Visual Studio 2008 (with .Net 3.5 SP1 installed)
  • Microsoft Expression Encoder 2 (with Expression Encoder 2 SP1 installed)

FTP Plug-in Overview

The FTP Publishing Plug-in for Encoder created by Tim Heuer allows the encoded output of to be “published” to a FTP server for hosting. One minor issue from a security point of view is the password is shown in clear text. As a result the password is always visible and it could represent a security risk.

In the next section I will look at replacing the standard WPF text TextBox control with a PasswordBox so that the password is masked from the user.

FTP Plug-in Changes

Looking at the original XAML it is a standard TextBox that makes use of data binding to load and save changes.

<Label Grid.Column="0" Grid.Row="2" HorizontalAlignment="Right" Margin="0,0,3,0" Content="Password" />
<TextBox Name="Password" Grid.Row="2" Grid.Column="1" ToolTip="FTP account password" Margin="0,2.5,0,2.5" Text="{Binding Path=Password, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />

The obvious change to replace this with a PasswordBox and bind to the Password property instead of the Text property.

        <Label Grid.Column="0" Grid.Row="2" HorizontalAlignment="Right" Margin="0,0,3,0" Content="Password" />
        <PasswordBox  Name="Password" Grid.Row="2" Grid.Column="1" ToolTip="FTP account password" Margin="0,2.5,0,2.5" Password="{Binding Path=Password, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />

This however generates an error. Looking at the problem further, Samuel Jack provides the explanation in his post WPF PasswordBox and Data binding. In the post he discusses how the Password property is a plain CLR property rather than a Dependency Property, so it doesn't support being the target of a data binding. Using Samuel’s solution the code is easily modified to make use of attached properties by adding a attribute namespace reference at the top of the UserControl

xmlns:FtpPublish="clr-namespace:TimHeuer.Expression.FtpPublish"

Combined with additions to the the PasswordBox xaml to make use of the attached properties

<Label Grid.Column="0" Grid.Row="2" HorizontalAlignment="Right" Margin="0,0,3,0" Content="Password" />
<PasswordBox  Name="Password" Grid.Row="2" Grid.Column="1" ToolTip="FTP account password" Margin="0,2.5,0,2.5" FtpPublish:PasswordBoxAssistant.BindPassword="true" FtpPublish:PasswordBoxAssistant.BoundPassword="{Binding Path=Password, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />

And adding the following C# class to define the implementation of the PasswordBoxAssistant class.

using System.Windows;
using System.Windows.Controls;

namespace TimHeuer.Expression.FtpPublish
{
    /// <summary>
    /// Class that allows a <see cref="PasswordBox"/> to be included in DataBinding using attached properties
    /// </summary>
    /// <remarks>
    /// Original source http://blog.functionalfun.net/2008/06/wpf-passwordbox-and-data-binding.html
    /// </remarks>
    public static class PasswordBoxAssistant
    {
        public static readonly DependencyProperty BoundPassword =
            DependencyProperty.RegisterAttached("BoundPassword", typeof(string), typeof(PasswordBoxAssistant), new FrameworkPropertyMetadata(string.Empty, OnBoundPasswordChanged));

        public static readonly DependencyProperty BindPassword = DependencyProperty.RegisterAttached(
            "BindPassword", typeof(bool), typeof(PasswordBoxAssistant), new PropertyMetadata(false, OnBindPasswordChanged));

        private static readonly DependencyProperty UpdatingPassword =
            DependencyProperty.RegisterAttached("UpdatingPassword", typeof(bool), typeof(PasswordBoxAssistant));

        private static void OnBoundPasswordChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var box = d as PasswordBox;

            // only handle this event when the property is attached to a PasswordBox
            // and when the BindPassword attached property has been set to true
            if (box == null || !GetBindPassword(d))
            {
                return;
            }

            // avoid recursive updating by ignoring the box's changed event
            box.PasswordChanged -= HandlePasswordChanged;

            var newPassword = (string)e.NewValue;

            if (!GetUpdatingPassword(box))
            {
                box.Password = newPassword;
            }

            box.PasswordChanged += HandlePasswordChanged;
        }

        private static void OnBindPasswordChanged(DependencyObject dp, DependencyPropertyChangedEventArgs e)
        {
            // when the BindPassword attached property is set on a PasswordBox,
            // start listening to its PasswordChanged event

            var box = dp as PasswordBox;

            if (box == null)
            {
                return;
            }

            var wasBound = (bool)(e.OldValue);
            var needToBind = (bool)(e.NewValue);

            if (wasBound)
            {
                box.PasswordChanged -= HandlePasswordChanged;
            }

            if (needToBind)
            {
                box.PasswordChanged += HandlePasswordChanged;
            }
        }

        private static void HandlePasswordChanged(object sender, RoutedEventArgs e)
        {
            var box = sender as PasswordBox;

            if (box == null)
                return;

            // set a flag to indicate that we're updating the password
            SetUpdatingPassword(box, true);
            // push the new password into the BoundPassword property
            SetBoundPassword(box, box.Password);
            SetUpdatingPassword(box, false);
        }

        public static void SetBindPassword(DependencyObject dp, bool value)
        {
            dp.SetValue(BindPassword, value);
        }

        public static bool GetBindPassword(DependencyObject dp)
        {
            return (bool)dp.GetValue(BindPassword);
        }

        public static string GetBoundPassword(DependencyObject dp)
        {
            return (string)dp.GetValue(BoundPassword);
        }

        public static void SetBoundPassword(DependencyObject dp, string value)
        {
            dp.SetValue(BoundPassword, value);
        }

        private static bool GetUpdatingPassword(DependencyObject dp)
        {
            return (bool)dp.GetValue(UpdatingPassword);
        }

        private static void SetUpdatingPassword(DependencyObject dp, bool value)
        {
            dp.SetValue(UpdatingPassword, value);
        }
    }
}

Testing The Changes

Copying the updated dll to the <Program Files>\Microsoft Expression\Encoder 2\Plugins folder, the plug-in now correctly loads but the password box does not have the correct visual styles applied to it because the PasswordBox has not been included in the template styles for the WPF plug-in.

Overall the control is missing the following functionality:

  • The correct background colour.
  • Rounded corners on the control border,
  • Missing a password character to display when characters are typed.

The next sections will look at adding applying styles to the PasswordBox so that it is consistent with the rest of the plug-in.

PasswordBox Styles

Adjusting The style of the password box can be performed by altering the UserControl resources of the StandardSettings.xaml. An example of how apply the rounded corners is provided by Tamir Khason on the msdn forums. Tweaking example slightly to only have a border radius of 2 gives the following xaml to be added to the xaml file.

<UserControl.Resources>
    <Style TargetType="PasswordBox">
        <Setter Property="PasswordChar" Value="●"/>
        <Setter Property="KeyboardNavigation.TabNavigation" Value="None"/>
        <Setter Property="BorderThickness" Value="1"/>
        <Setter Property="HorizontalContentAlignment" Value="Left"/>
        <Setter Property="Padding" Value="1"/>
        <Setter Property="FocusVisualStyle" Value="{x:Null}"/>
        <Setter Property="AllowDrop" Value="true"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="PasswordBox">
                    <Border CornerRadius="2" x:Name="Bd" Background="{DynamicResource WindowBackgroundBrush}" BorderBrush="Black" BorderThickness="1" OpacityMask="{x:Null}">
                        <ScrollViewer SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" x:Name="PART_ContentHost" Template="{DynamicResource ScrollViewerControlTemplate1}"/>
                    </Border>
                    <ControlTemplate.Triggers>
                        <Trigger Property="IsEnabled" Value="false">
                            <Setter Property="Background" TargetName="Bd" Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"/>
                            <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</UserControl.Resources>

Which gives the following output

This style is much closer, but it gives the impression that the password box is not editable by the user. In the next section I will make use Redgate’s .Net Reflector to determine the styles used by the Expression Encoder UI to provide a more consistent look and feel.

Investigating Default Styles

The expression encoder is written using Microsoft.Net and WPF. As result the styles and user interface as stored within the application assemblies and binary files. Using Redgates .Net Reflector together with the BAML viewer addin available on codeplex we can look inside these file and discover the styles used in the application.

The main WPF application is located in <Program Files>\Microsoft Expression\Encoder 2\EncoderUI.exe and the english resource files that include the xaml for the application is located in <Program Files>\Microsoft Expression\Encoder 2\en\EncoderUI.resources.dll. Opening these files in reflector we are able to look at the implementation of the application.

 

Looking at the xaml the style for a password box within the resources assembly the dynamic resource BackgroundBrush should be applied for the BorderBrush and the Background properties of the PasswordBox.

<Style x:Uid="Style_84" TargetType="{x:Type PasswordBox}">
    <Setter x:Uid="Setter_1349" Property="BorderBrush" Value="{DynamicResource BackgroundBrush}" />
    <Setter x:Uid="Setter_1350" Property="Foreground" Value="{DynamicResource Text1Brush}" />
    <Setter x:Uid="Setter_1351" Property="Background" Value="{DynamicResource BackgroundBrush}" />
    <Setter x:Uid="Setter_1146" Property="FontFamily" Value="{DynamicResource {x:Static MessageFontFamily}}" />
    <Setter x:Uid="Setter_1147" Property="FontSize" Value="{DynamicResource {x:Static MessageFontSize}}" />
    <Setter x:Uid="Setter_1148" Property="FontWeight" Value="{DynamicResource {x:Static MessageFontWeight}}" />
</Style>

Final Version

Applying the style changes in the last section provides the following UserControl.Resources section.

<UserControl.Resources>
    <Style TargetType="PasswordBox">
        <Setter Property="PasswordChar" Value="●"/>
        <Setter Property="KeyboardNavigation.TabNavigation" Value="None"/>
        <Setter Property="BorderThickness" Value="1"/>
        <Setter Property="HorizontalContentAlignment" Value="Left"/>
        <Setter Property="Padding" Value="1"/>
        <Setter Property="FocusVisualStyle" Value="{x:Null}"/>
        <Setter Property="AllowDrop" Value="true"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="PasswordBox">
                    <Border CornerRadius="2" x:Name="Bd" Background="{DynamicResource BackgroundBrush}" BorderBrush="{DynamicResource BackgroundBrush}" BorderThickness="1" OpacityMask="{x:Null}">
                        <ScrollViewer SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" x:Name="PART_ContentHost" Template="{DynamicResource ScrollViewerControlTemplate1}"/>
                    </Border>
                    <ControlTemplate.Triggers>
                        <Trigger Property="IsEnabled" Value="false">
                            <Setter Property="Background" TargetName="Bd" Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"/>
                            <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</UserControl.Resources>

With an output of

Review

So there we have it, the plug-in look and feel was updated by altering the XAML to make use of Password box. The changes required the use of WPF attached properties to ensure the data binding continues to work with he original code. To make the user interface look consistent with rest of the plug-in UserControl resources where used to apply the correct styles and resources.

# Saturday, January 24, 2009

Mix09 10k Contest Entry – Spin And Win – Information And Help

Spin And Win Thumbmail10K Logo

Introduction

Welcome to Spin & Win a Microsoft Silverlight Game created for the MIX09 10k Challenge.

Spin & Win is a multimedia slot machine game that starts with an initial credit balance of 500. The game keeps a record of your current balance so you can start it again and continue from where you left off.

Just select the amount you want to bet and press the SPIN button to begin, Good Luck!

Game Description

Loading

Initially as the game loads, it will display a loading text with a percentage complete. Once the game is loaded the loading text will disappear and the game will start.

Spin & Win Screenshot

Once the game has been started by clicking on SPIN you can use the Enter key, or change your bet & spin again.

Example Current Status

There are 3 horizontal lines can be played with the default being line 1 only. You can change either the number of lines to place a bet on or the credits per line by clicking on the buttons at the bottom of the game. The selected amount is indicated by a lighter grey button.

Example Number Of Lines Example Number Of Lines

If a play line is not selected it will be greyed out to indicate that it will not be included in a win.

Example of online slot lines 1 selected Example of all slot lines in the game selected

Example: Line 1 Selected, All Lines Selected

If you want to "cash-in" your virtual credits you can click on the PAYOUT button. This will reset the credits total to zero and ask you if you wish to play again.

Example Number Of Lines

Win

In the example below 3 lines have been selected to play, with 10 credits per line.

Example Payout

On the top line there 3 apples. The total win amount is 3 Apples (10 Credits) x 10 Credits Per Line = 100 Credits.

Other Controls

The information icon in the bottom left will open this page.

Information Icon

The volume on/off will toggle the audio sound for the reel spin, winning fanfare and payout.

Toggle Volumne On/Off

The social bookmark icons under the logo allow you to easily share and link to the game using Delicious, Digg, StumbleUpon, Technorati, Twitter and Email. NOTE: All the trademarks and logos are the property of their respective owners.

Toggle Volumne On/Off

Multimedia Attributions

The skin for the game uses the following media items:

Info Icon

By Mazenl77 - Creative Commons Attribution 3.0 Unported.

1603 PNG

Samples used from Freesound

January 19, 2009

By dobroide - Creative Commons License

slot.machine.arcade.mp3

Social Bookmark Icons

By mao - Creative Commons Attribution 3.0 License.

Speaker Icon

By Mazenl77 - Creative Commons Attribution 3.0 Unported.

256x256px PNG

# Friday, January 23, 2009

My Mix09 10k Contest Entry Is Live – Spin And Win

Inspired by Mix09 being held in Las Vegas the logical choice of entry was a slot machine game. So my Silverlight entry Spin & Win was created.

Spin And Win Screenshot

You can play the game online at the Mix09 Site and information about the game is linked within the game and is also available from my Mix09 10k Information/Help page.

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:

  • Animation of controls to create visual effects.
  • Coding optimisation techniques to minimise the size of the code.
  • Dynamically loading controls and components.
  • External links from the application e.g. Social bookmarks and Information/Help on the game.
  • Graphic design of the game.
  • Interaction with external services.
  • State management giving the ability to save and load the state of the game.

Visit the  Mix site now and add a vote and I look forward to your comments.

# Friday, December 19, 2008

Silverlight BabySmash Audio Files

Overview

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.

The main sections are:

  • Introduction – Discusses the background behind the use of audio files in the original WPF version and an overview of the changes required.
  • Code Changes – Discusses the changes required to move from win32 api implementation to the MediaElement.
  • Audio File Changes – Discusses uses Microsoft Expression Media Encoder 2 to convert the wav files to wma files.
  • Summary – Overview of the changes made and the advantages of the change.

Introduction

As Scott indicated in his first post Introducing BabySmash, 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.

Code Changes

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.

    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<string, string> cachedWavs = new Dictionary<string, string>();
        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);
        }

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.

    public class XamlAudio
    {
        private MediaElement player;
        readonly Queue<string> soundsToPlay = new Queue<string>();
        private bool inPlay;

        private Dictionary<string, Stream> media = new Dictionary<string, Stream>();

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

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

            PlaySound(s);
        }

The second major difference in the code is the shift from  the use of embedded audio files, as indicated in my earlier post Silverlight BabySmash Performance – The Asynchronous Story, the Silverlight version asynchronously downloads the audio files on demand. As result the initial download size for the deployed application is smaller.

Audio File Changes

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.

The steps I used to convert the files where as follows:

  1. Download and install Microsoft Expression Encoder 2.
  2. Open Expression Encoder.
  3. Import the existing wmv files. Either using File Import menu or by licking on the Import button at the bottom of the window.
  4. Select the Encode tab.
  5. In the profile audio group expand the advanced properties.
  6. Change the properties to a smaller size to optimise the smaller size.
    1. Codec WMA
    2. Mode CBR
    3. Bitrate 20kps
    4. Sample Rate 44.1kHz
    5. Bits Per Sample 16
    6. Channels Mono
  7. Start the encoding process by click the encode button at the bottom of the window or the Encode File menu item.
  8. Once the encoding is complete select Output tab in the top right.
  9. Open the output folder by clicking o the Open Job button in the Job Output Section.

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.

File WAV Encoding (kbps) WAV Files Size (KB) WMA Encoding (kbps) WMA File Size (KB)
babygigl2 20 6 20 15
babylaugh 88 14 20 11
ccgiggle 88 9 20 10
EditedJackPlaysBabySmash 352 227 20 25
falling 128 29 20 14
giggle 88 15 20 13
laugingmice 89 17 20 14
rising 128 26 20 13
scooby2 20 5 20 13
smallbumblebee 705 862 20 31

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.

Summary

  • Changes to the C# code to make use of the built in MediaElement to play the audio files.
  • Cross platform audio support using wma files instead of wmv files.
  • Use of Expression Encoder to convert the source wmv files to a web optimised encoding.
# Sunday, November 16, 2008

Silverlight BabySmash Performance – The Asynchronous Story

Overview

In this blog entry I will cover some the factors effecting perceived performance and the asynchronous code used to optimise the user experience.

Introduction

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:

  • Initial download size.
  • Use of a secondary assembly.
  • Use of media files.

So let's start of by profiling the current application see what has been done.

Http Download Profile

Initial DownloadInitial DownloadSecondary DownloadSecondary DownloadLoad On DemandLoad On Demand

Looking at the HTTP profile of the application there are three distinct phases:

  1. Synchronous download of the initial html code and the Silverlight application.
  2. Asynchronous download of secondary components.
  3. Asynchronous download of media files on demand as the user presses keys.

Initial Download

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.

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.

    <div id="silverlightControlHost">
        <object data="data:application/x-silverlight-2," type="application/x-silverlight-2" width="100%" height="100%">
            <param name="source" value="ClientBin/BabySmashWeb.xap"/>
            <param name="onerror" value="onSilverlightError" />
            <param name="background" value="white" />
            <param name="minRuntimeVersion" value="2.0.31005.0" />
            <param name="autoUpgrade" value="true" />
            <param name="initParams" value="logKeys=true" />
            <a href="http://go.microsoft.com/fwlink/?LinkID=124807" style="text-decoration: none;">
	            <img src="http://go.microsoft.com/fwlink/?LinkId=108181" alt="Get Microsoft Silverlight" style="border-style: none"/>
            </a>
        </object>
        <iframe style='visibility:hidden;height:0;width:0;border:0px'></iframe>
    </div>

Secondary Download

Once the application is loaded, the constructor of the main code behind Page class is called

        public Page()
        {
            InitializeComponent();

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

            KeyUp += new KeyEventHandler(Page_KeyUp);

            LayoutRoot.Children.Add(mainPage);
        }

Which calls starts a Controller class, which in turn starts the asynchronous requests for the letters and the initial media file.

       public void Launch()
       {
           CheckForUpdatesAsync();

           SetupInitialWindowState();

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

           //Startup sound
           audio.PlayWavResourceYield(".Resources.Sounds." + "EditedJackPlaysBabySmash.wav");
       }

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 Load On Demand section below.

   public partial class CoolLetter 
	{
      public CoolLetter()
      {
         this.InitializeComponent();
      }

	    static readonly Dictionary<string, string> _letters = InitLetters();

        public static void InitLetterStateAsync()
        {
            // No work to do as the private _letters will be initialised above
        }

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

       private static Dictionary<string, string> InitLetters()
       {
           var addressUri = new Uri("BabySmashEnglishLetters.xap", UriKind.Relative);
           var letterLoader = new WebClient();
           letterLoader.OpenReadCompleted += letterAssemblyLoaded;
           letterLoader.OpenReadAsync(addressUri);
           return new Dictionary<string, string>();
       }

Once the asynchronous call is completed, reflection is used to dynamically call the GenerateAlphanumericCharacters method of the AlphaNumericLetterGenerator class.

	    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<string, string>;
            if ( generated == null ) return;
	        foreach (var pair in generated)
	        {
	            _letters.Add(pair.Key, pair.Value);
	        }
	    }
Loading the assembly is achieved by extracting the requested assembly from the xap stream as follows.

	    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;
        }

Load On Demand

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.

Loading the external dependencies has also given us the following advantages:

  • Allow for the possibility of localising the application to other non-english languages.
  • Allow media files to be updated independently of the compiled code.
  • Allow the media files to to be sourced from alternative locations e.g. File stored locally on the client computer.
  • Reduced bandwidth from 406kb for users that visit the page but do not start using the application.

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.

        public void PlayWavResourceYield(string s)
        {
            if (player == null)
                return;

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

            PlaySound(s);
        }

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.

        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);
        }

If the media file is requested again then the version available in the media list will be reused and played to the user.

Summary

In summary when looking Silverlight applications one aim is to improve the perceived user performance by:

  • 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.
  • 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.
  • 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.