This post is part of a series of blog posts and videos about the Task-It (task management) application that I have been building with Silverlight 4 and Telerik's RadControls for Silverlight 4. For a full index of these resources, please go here. One of the posts listed in the index provides a full source download for the application.The need for lazy loading
In my MEF into post, MEF to the rescue in Task-It, I outlined a couple of issues I was facing and explained why I chose MEF (the Managed Extensibility Framework) to solve these issues.
The first issue was the fact that my application startup time was getting slower and slower as the size of the app grew. My app currently only has 4 pages, but as a user enters the app, they may only hit only one of those pages, so why load the code for all 4 if they will never be used. If they could be ‘lazy loaded’, it would increase the startup time (and I wouldn’t have to wait as long every time I start up the app during the development process).
Other posts to check out
There are a few other resources out there around dynamic XAP loading that you may want to review (by the way, Glenn Block is the main dude when it comes to MEF):
These provide some great info, but didn’t exactly cover the scenario I wanted to achieve in Task-It…and that is dynamically loading each of the app’s pages the first time the user enters a page.
In the code I provided for download above, I created a simple solution that shows the technique I used for dynamic XAP loading in Task-It, but without all of the other code that surrounds it. Taking all that other stuff away should make it easier to grasp. Having said that, there is still a fair amount of code involved. I am always looking for ways to make things simpler, and to achieve the desired result with as little code as possible, so if I find a better/simpler way I will blog about it, but for now this technique works for me.
When I created this solution I started by creating a new Silverlight Navigation Application called DynamicXAP Loading. I then added the following line to my UriMappings in MainPage.xaml:
In the section of MainPage.xaml that produces the page links in the upper right, I kept the Home link, but added a couple of new ones (page1 and page 2). These are the pages that will be dynamically (lazy) loaded:
In App.xaml.cs I added a bit of MEF code. In Application_Startup I call a method called InitializeContainer, which creates a PackageCatalog (a MEF thing), then I create a CompositionContainer and pass it to the CompositionHost.Initialize method. This is boiler-plate MEF stuff that allows you to do 'composition' and import 'packages'. You're welcome to do a bit more MEF research on what is happening here if you'd like, but for the purpose of this example you can just trust that it works. :-)
In the sample code you'll notice that there is a project in the solution called DynamicXAPLoading.Infrastructure. This is simply a Silverlight Class Library project that I created just to move stuff I considered application 'infrastructure' code into a separate place, rather than cluttering the main Silverlight project (DynamicXapLoading). I did this same thing in Task-It, as the amount of this type of code was starting to clutter up the Silverlight project, and it just seemed to make sense to move things like Enums, Constants and the like off to a separate place.
In the DynamicXapLoading.Infrastructure project you'll see 3 classes:
Adding a new page
We currently have a couple of pages that are being dynamically (lazy) loaded, but now let's add a third page.
1. First, create a new Silverlight Application project:
In this example I call it Page3. In the future you may prefer to use a different name, like DynamicXAPLoading.Page3, or even DynamicXAPLoading.Modules.Page3. It can be whatever you want. In my Task-It application I used the latter approach (with 'Modules' in the name). I do think of these application as 'modules', but Prism uses the same term, so some folks may not like that. Use whichever naming convention you feel is appropriate, but for now Page3 will do.
When you change the name to Page3 and click OK, you will be presented with the Add New Project dialog:
It is important that you leave the 'Host the Silverlight application in a new or existing Web site in the solution' checked, and the .Web project will be selected in the dropdown below. This will create the .xap file for this project under ClientBin in the .Web project, which is where we want it.
2. Uncheck the 'Add a test page that references the application' checkbox, and leave everything else as is.
3. Once the project is created, you can delete App.xaml and MainPage.xaml.
4. You will need to add references your new project to the following:
If you have installed the latest RC bits you will find the last 3 dll's under the .NET tab in the Add Referenced dialog. They live in the following location, or if you are on a 64-bit machine like me, it will be Program Files (x86).
C:\Program Files\Microsoft SDKs\Silverlight\v4.0\Libraries\Client
Now let's create some UI for our new project.
5. First, create a new Silverlight User Control called Page3.dyn.xaml
6. Paste the following code into the xaml:
This is just a 'shim', part of David Poll's technique for dynamic loading.
7. Expand the icon next to Page3.dyn.xaml and delete the code-behind file (Page3.dyn.xaml.cs).
8. Next we will create a control that will 'host' our page. Create another Silverlight User Control called Page3Host.xaml and paste in the following XAML:
9. Now paste the following code into the code-behind for this control:
Notice that we are now using that PageMetadata custom attribute class that we created in the Infrastructure project, and setting its two properties.
10. Go back to that Enums.cs under the Infrastructure project and add a 3rd entry for Page3:
11. Now right-click on the Page3 project and add a folder called Views.
12. Right-click on the Views folder and create a new Silverlight User Control called Page3.xaml. We won't bother creating a view model for this User Control as I did in the Page 1 and Page 2 projects, just for the sake of simplicity. Feel free to add one if you'd like though, and copy the code from one of those other projects. Right now those view models aren't really doing anything anyway...though they will in my next post. :-)
13. Now let's replace the xaml for Page3.xaml with the following:
14. And in the code-behind remove the inheritance from UserControl, so it should look like this:
One thing you may have noticed is that the base class for the last two User Controls we created is DynamicPage. Once again, we are using the infrastructure that David Poll created.
15. OK, a few last things. We need a link on our main page so that we can access our new page. In MainPage.xaml let's update our links to look like this:
16. Next, we need to add the following at the bottom of MainPageViewModel in the ViewModels directory of our DynamicXAPLoading project:
17. And at last, we need to add a case for our new page to the switch statement in MainPageViewModel:
Now fire up the application and click the page 1, page 2 and page 3 links. What you'll notice is that there is a 2-second delay the first time you hit each page. That is because I added the following line to the Navigate method in MainPageViewModel:
The reason I put this in there is that I wanted to simulate a delay the first time the page loads (as the .xap is being downloaded from the server). You'll notice that after the first hit to the page though that there is no delay...that's because the .xap has already been downloaded. Feel free to comment out this 2-second delay, or remove it if you'd like. I just wanted to show how subsequent hits to the page would be quicker than the initial one.
By the way, you may want to display some sort of BusyIndicator while the .xap is loading. I have that in my Task-It appplication, but for the sake of simplicity I did not include it here. In the future I'll blog about how I show and hide the BusyIndicator using events (I'm currently using the eventing framework in Prism for that, but may move to the one in the MVVM Light Toolkit some time soon).
Whew, that felt like a lot of steps, but it does work quite nicely. As I mentioned earlier, I'll try to find ways to simplify the code (I'd like to get away from having things like hard-coded .xap file names) and will blog about it in the future if I find a better way.
In my next post, I'll talk more about what is actually happening with the code that makes this all work.
Subscribe to be the first to get our expert-written articles and tutorials for developers!
All fields are required