In this episode we will have a look at the intricacies of using multiple XAP files for our application. As you may remember from the first post, we expect this technique to help us reach some of the requirement goals we have, but we also identified some potential pitfalls with it.
As we implemented the basics of this we obviously ran into some issues that need solving. Below follow some topics to think about.
Silverlight will handle only one connection at any time
This may not seem obvious at first. If you haven't loaded a XAP file at runtime, you won't know this, so I figured I would give this some explanation. Whenever you load a XAP file you use the WebClient class to get the XAP file from the webserver. As this is done asynchronously, as any other communication, any other code you may invoke will simply execute will the XAP file is being downloaded. However, Silverlight will only allow you to have one connection at any time, so downloading multiple XAP files, or invoking any service calls for that mather, will throw an exception (as documented here).
As we load multiple XAP files at startup, one XAP file has to be downloaded after another. There are a couple of ways to solve this and it realy depends on the application design and requirements, but one thing is for sure: you don't want to bload this.
We chose to have an approach where one load completion leads to another load. In other words, one XAP files load is triggered by another XAP files completion event. We can do this, because one class, and one class only, is responsible for loading any XAP files and we were able to determine an order to load things in that will always work.
Another aproach you may want to consider if the above don't apply to your application (e.g. you can't determine a fixed order to load modules) is to have a queue for loading modules, where the completion of loading a module from the queue can trigger the loading of the next module, etc..
Loading UIElements from XAP files
But getting the XAP file is obviously only the first step in the process. The next step is to actually load the assemblies inside and, in our case at least, get a UIElement from one of these assemblies. To achieve this, we wrote a XapHelper class, that contains a static method to assist in the loading. The code looks like this:
public class XapHelper
private const string AppManifestFileName = "AppManifest.xaml";
private const string AssemblySourceAttributeName = "Source";
private const string ApplicationBinaryContentTypeName = "application/binary";
internal static UIElement LoadUIElementFromXap(Stream xapStream, string assemblyName, string className)
string applicationManifest = ReadApplicationManifest(xapStream);
XElement deploymentElement = XDocument.Parse(applicationManifest).Root;
IEnumerable<XElement> deploymentParts = from assemblyParts in deploymentElement.Elements().Elements()
Assembly assembly = LoadAssemblies(xapStream, assemblyName, deploymentParts);
UIElement element = assembly.CreateInstance(className) as UIElement;
private static Assembly LoadAssemblies(Stream xapStream, string assemblyName, IEnumerable<XElement> deploymentParts)
Assembly assembly = null;
foreach (XElement assemblyPartElement in deploymentParts)
string name = assemblyPartElement.Attribute(AssemblySourceAttributeName).Value;
AssemblyPart assemblyPart = new AssemblyPart();
StreamResourceInfo assemblyResourceInfo = new StreamResourceInfo(xapStream, ApplicationBinaryContentTypeName);
Uri assemblyUri = new Uri(name, UriKind.Relative);
StreamResourceInfo streamInfo = Application.GetResourceStream(assemblyResourceInfo, assemblyUri);
if (name == assemblyName)
assembly = assemblyPart.Load(streamInfo.Stream);
private static string ReadApplicationManifest(Stream xapStream)
StreamResourceInfo manifestResourceInfo = new StreamResourceInfo(xapStream, null);
Uri appManifestUri = new Uri(AppManifestFileName, UriKind.Relative);
StreamResourceInfo applicationResourceStream = Application.GetResourceStream(manifestResourceInfo, appManifestUri);
Stream resourceStream = applicationResourceStream.Stream;
StreamReader streamReader = new StreamReader(resourceStream);
string applicationManifest = streamReader.ReadToEnd();
As you can see, the main method here is LoadUIElementFromXap and you have to provide it with a stream that contains the XAP file. This stream would be the result of loading it trough the WebClient class. The second parameter is the name of the assembly to load the UIElement from and the third paremeter is the classname for the UIElement.
Also you may have noticed that the code is a little more verbose than some of the samples online. This is done to improve readability and maintainability of the code.
Identify XAP file split-ups
Another obvious question you will run into when using this aproach is, how will I split up my XAP files?
One way to look at it is to have a good look at your general screen design. What parts are always, or at least usually, the same? What parts change a lot?
Another important factor is size. After all, it all started out because of performance. So ask yourself, what parts of the interface are likely to become large and are not all needed at the same time?
Whatever rules you apply to this, one fact is very important. You should identify responsibilities that you need to host in the XAP files. What I mean by that is, you need to make sure you don't split up code that handles a single responsibility, breaking your OO design. Posible subjects for this are:
- Loading XAP files
- Data connections (altough this one is not likely)
- User settings
And obviously there could be many more, depending on the application your building.
In our case, loading XAP files and security were assigned to the outer XAP file as a responsibility. As we choose to have some navigation assigned to another XAP file, that resulted in loading the first, we created an interface, called ILoadModules, that identifies UserControls that want to load XAP files and supply the main XAP file with a delegate that it can set, to provide a method of doing so.
As I promised dsoltesz earlier I've uploaded a sample project.
I hope you liked the more technical side of this artice and I'm happy to have actually squezed in some code ;-). Please let me know if you have any questions, comments, etc..