My Favorite Silverlight Extensions APIs - Part 1

SilverlightAs some of you may know, I am a contributor to the SilverlightContrib open source project. Recently this project and the Silverlight Extensions open source project (also know as SLExtensions) decided to merge to create a single place for a lot of interesting functionality.

I want to highlight some of the pieces that I didn't contribute as I've seen a lot of really great functionality for those of you who are building Silverlight Applications.  I honestly have no idea how many parts this blog series will be, as long as I have parts that are interesting, I'll continue to post about them.

First up is SLExtension's Bootstrapper functionality. If you are currently using Prism or a related technology to compose your application at runtime, this functionality is lost on you. The Bootstrapper functionality is for that middle-ground where you want to break up your project into a small number of .xap files that are loaded when the project loads up but don't need a full composition engine with MEF or Prism.

The Bootstrapper functionality is in a separately assembly in the SLExtensions project (SLExtensions.Bootstrapping.dll). This is a small assembly (so that your main project loads fast). The way it works is to have you change the derived class for the App.xaml to a BootstrapApplication:

public partial class App : BootstrapApplication
{

Since its the App.xaml class, you'll need to change the XAML as well:

<bs:BootstrapApplication 
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  x:Class="FunWithBootstrapper.App"
  xmlns:bs="clr-namespace:SLExtensions.Bootstrapping;
            assembly=SLExtensions.Bootstrapping">
  <bs:BootstrapApplication.Resources>

  </bs:BootstrapApplication.Resources>
</bs:BootstrapApplication>

Once you have the application changed, you will need to override the Xaps property to supply the list of XAP files to load:

protected override IEnumerable<Uri> Xaps
{
  get
  {
    // Must be absolute Uris so we steal it from the host
    return new Uri[] { 
      new Uri(new Uri(Host.Source.AbsoluteUri), "SecondApp.xap"),
      new Uri(new Uri(Host.Source.AbsoluteUri), "ThirdApp.xap"),
    };
  }
}

At this point it will load up the other .xap files but we won't be able to do anything with them. So let's look at the other projects briefly.

The other <a href='http://wildermuth.com/silverlight' target='_blank'>Silverlight</a> Projects

The other Silverlight projects are actually full Silverlight Applications (not Client Library projects). The Bootstrapper is usually used to create a 'splash screen' xap file that loads the other parts of the applications.  Again, not real composition like Prism, but something less than that.

Project Reference Dialog

In the main project, you will make a reference to the other application projects. That's how you'll be able to reference the code from the other xap files (though only once they've been loaded).

To help you know when its updated, the Bootstrapper application has two facilities:

  • Progress event
  • OnApplicationReady overridable method

Now that you have a bootstrapping application, you want to be able to work with it. The first thing I usually do (because I want to use the Bootstrapping API's from anywhere without casting) is create a new static property on the Application class:

// Better access to the bootstrapping app
public static App MyApp
{
  get { return Current as App; }
}

Then I can use the Progress event to show the user some progress like so:

void MainPage_Loaded(object sender, RoutedEventArgs e)
{
  App.MyApp.Progress += 
    new EventHandler<BootstrapEventArgs>(Bootstrap_Progress);
}

void Bootstrap_Progress(object sender, BootstrapEventArgs e)
{
  thePartProgress.Value = e.StepProgress;
  theFullProgress.Value = e.OverallProgress;
}

I'd like to be able to know when the application is completely loaded too. Unfortunately the Progress event tends to not return 100% at any time.  I haven't figured out why yet, but to get around that you can use the OnApplicationReady overridable method:

public partial class App : BootstrapApplication
{
  ...
  
  protected override void OnApplicationReady(StartupEventArgs e)
  {
    // Do startup behavior
  }
  
  ...
}

While this works, I often don't want to do this work in the App class, but instead in other parts of my application (e.g. MainForm.xaml.cs). So For my use, I usually expose an event as well:

public event EventHandler<StartupEventArgs> StartupComplete;

protected override void OnApplicationReady(StartupEventArgs e)
{
  if (StartupComplete != null)
  {
    StartupComplete(this, e);
  }
}

By throwing an event when the application is ready, I can register for this event and do the startup once I know all the code is available:

// MainPage.xaml.cs
void MainPage_Loaded(object sender, RoutedEventArgs e)
{
  App.MyApp.Progress += 
    new EventHandler<BootstrapEventArgs>(Bootstrap_Progress);

  App.MyApp.StartupComplete += 
    new EventHandler<StartupEventArgs>(Bootstrap_StartupComplete);
}

void Bootstrap_StartupComplete(object sender, StartupEventArgs e)
{
  // Use the loaded types 
  // (SecondApp.MainPage and ThirdApp.MainPage)
  theSecondApp.Children.Add(new SecondApp.MainPage());
  theThirdApp.Children.Add(new ThirdApp.MainPage());
}

Note that once the bootstrapping has completed you can use the assemblies in the other .xap projects. If you attempt to use them before, you'll get an exception that it couldn't load the type.

Just to prove the point, here is a list of the .xap files.  Notice that the 2nd and 3rd .xap file are pretty big but even with the bootstrapping assembly, our code is only 10K which means it should load very fast:

Relative XAP Sizes

Neat?  What do you think?

You can get the source to my example here:

http://wildermuth.com/downloads/FunWithBootstrapper.zip  

 

Comments:

Gravatar

It's neat, but if you just want a bootstrapper I would just use the Prism bootstrapper and none of the other parts.

Gravatar

The problem with using Prism's Bootstrapper alone is it requires Unity for DI and can only do loading of Modules, not .xaps (so you have to define all your .dlls and make them into modules to get them to load).

In this bootstrapper it loads the .xap file and uses the manifest to load all the assemblies.

Gravatar

The Prism bootstrapper can load xap's as long as they use the module technique (haivng a module class file that implements the module interface). I just don't see the effort involved in this to be an issue. While I like the open source idea here, I prefer the DI approach that Prism uses and the PnP code.

On the other hand, the cool factor is pretty high to me of the approach you show. It's just solving a problem I already have a solution to. So for me, not so interested. But options are always good :-)

Gravatar

VS output window - 6 compile errors. I pasted one below.
C:\Users\mike\Documents\Visual Studio 2008\Projects_SL3\FunWithBootstrapper\FunWithBootstrapper\obj\Debug\App.g.cs(12,7): error CS0246: The type or namespace name 'SLExtensions' could not be found (are you missing a using directive or an assembly reference?)

Gravatar

Sorry Mike, try the newest download. It has the assembly included.

Gravatar

It works as designed and very nicely too, I do say. Now I'll try it out with my apps.
This is just what I need to go with my "Simple Silverlight Framework" I don't want to learn prizm write now, but I do need fast primary downloads.

Shawn, this is very cool!

And THANK YOU!
Mike

Gravatar

@John Papa, not to beat a dead horse, but he prefaced the article by saying that if you use Prism already, this might not be of interest to you.

We built our own, but if this turns out to be cleaner, we'll look at it.

Also, Prism is overkill for small projects, we only use it on larger stuff(currently only one project), but most projects simply don't need Prism(IMO).

Gravatar

One last thing. When we built our own solution, on thing saved us in Silverlight 3, Merged Resource Dictionaries.

Without this feature, loading xap's was a nightmare of trying to extract the resources.

Merged Resource Dictionaries solved this problem.


 



 
Save Cancel