Loading XAPS On Demand in Silverlight (MEF)



I was reading about loading Multiple XAPs in Silverlight. This is a very usefull feature since it makes creation of dynamic content very easy.

I checked out the article http://blog.weareon.net/how-to-load-multiple-xap-files-in-silverlight-application/

Let us extend this article using MEF.

Also it would be good if you can go through the MSDN article on Downloading content on Demand http://msdn.microsoft.com/en-us/library/cc189021(v=vs.95).aspx


What I have done with this article is simply add Mef to it so that we can better control how we want to load our XAP.

Let me just walk through the steps which I have borrowed from Kranthi Gullapalli in his article. Thanks Kranthi for your help.

Create a new Silverlight project as MultipleXaps. Let us choose the Silverlight version as 4. Do make sure to choose a web project to deploy our silverlight project.

Make sure you add references for the following DLLs. These are necessary for Meffing. The solution should look as below:

XAPS On demand in Silverlight

The following is the code for MainPage.xaml.cs.

public partial class MainPage : UserControl
{

    public MainPage()
    {
        InitializeComponent();
        WebClient wc = new WebClient();
        wc.OpenReadCompleted += new OpenReadCompletedEventHandler(wc_OpenReadCompleted);
            wc.OpenReadAsync(new Uri(cls.xap, UriKind.Relative));

        }

        void wc_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e)
        {
            UIElement element = GetUIElementFromXap(e, cls.applicationName, cls.control);
            LayoutRoot.Children.Add(element);
        }

        private static UIElement GetUIElementFromXap(OpenReadCompletedEventArgs e, string applicationName, string control)
        {
            StreamResourceInfo resourceInfoDLL = Application.GetResourceStream(new StreamResourceInfo(e.Result, null),
                                                 new Uri(string.Format("{0}.dll", applicationName), UriKind.Relative));
            AssemblyPart assemblyPart = new AssemblyPart();
            Assembly assembly = assemblyPart.Load(resourceInfoDLL.Stream);
            UIElement element = assembly.CreateInstance(string.Format("{0}.{1}", applicationName, control)) as UIElement;
            return element;
        }

}

Let us break down the above program:

1. Let's dig into the constructor.

public MainPage()
{
    InitializeComponent();

// Create an instance of WebClient – The WebClient Provides common methods for sending data to and receiving data from a resource identified by a URI. 

    WebClient wc = new WebClient();

 //  OpenReadCompleted Event -Occurs when an asynchronous resource-read operation is completed.

    wc.OpenReadCompleted += new OpenReadCompletedEventHandler(wc_OpenReadCompleted);

// In the below step we call the method asynchronously as we do usually in Silverlight  - Note we pass the xap name and its relative path in this method .

   wc.OpenReadAsync(new Uri(cls.xap, UriKind.Relative));
 
}


2. Now let's check out the method GetUIElementFromXap – this is a custom method to return the UIElement from the XAP.

Note the parameters which are passed to the method:

  1. OpenReadCompletedEventArgs:

  2. ApplicationName - Which would be the application name in the XAP we are trying to load on demand.

  3. Control – Control from the XAP we are trying to load. 
     

private static UIElement GetUIElementFromXap(OpenReadCompletedEventArgs e, string applicationName, string control)
{
 

// In the below line of Code I am passing all the three parameters to create an instance of StreamResourceInfo  which Stores information for a stream resource .

            StreamResourceInfo resourceInfoDLL = Application.GetResourceStream(new StreamResourceInfo(e.Result, null),
                                                 new
Uri(string.Format("{0}.dll", applicationName), UriKind.Relative));
 
// An assembly part is an assembly that is to be included in a Silverlight-based application package (.xap).

            AssemblyPart assemblyPart = new AssemblyPart();
// Converts a Stream to an Assembly that is subsequently loaded into the current application domain.

            Assembly assembly = assemblyPart.Load(resourceInfoDLL.Stream);

// Once the Assembly is loaded we can get the UIElement out of it by Creating a Instance and passing the applicationName and Control Name to it .

            UIElement element = assembly.CreateInstance(string.Format("{0}.{1}", applicationName, control)) as UIElement;
 
// Finally return the UIElement .
 
            return element;
}


Creating the Plugin to be loaded on Demand:

We can now go ahead and create a simple Plugin as shown below. I just name it as SilverlightApplication1.

XAPS On demand in Silverlight

The XAML code would look like as shown below:

<UserControl x:Class="SilverlightApplication1.Control"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    d:DesignHeight="300" d:DesignWidth="400" Height="200" Width
="200">
    <Grid x:Name="LayoutRoot">
       <Border Background="#FFD4BBBB" CornerRadius="5">
              <TextBlock Text="Loaded from Xap 1" Height="16" Width="131" TextAlignment="Center" />
       </Border>
    </Grid>
</
UserControl>

Creating the Plugin Manager:

I just created a Silverlight Library Project and named it Plugin Manager. Create a class class1. The code for the class looks as below:

  [Export]
    public class Class1
    {
        public string xap { get; set; }

        public string applicationName { get; set; }

        public string control { get; set; }

        [ImportingConstructor]
        public Class1()
        {

            xap = "SilverlightApplication1.xap";
 
            applicationName = "SilverlightApplication1";
 
            control = "Control";
        }
    }

Add the Library Reference to our MultipleXaps project.


Modify the MainPage.xaml.cs in the MultipleXaps project to enable MEF:

    public partial class MainPage : UserControl
    {
        [Import]
        public Class1 cls { get; set; }

        public MainPage()
        {
            InitializeComponent();
 
            CompositionInitializer.SatisfyImports(this);

            WebClient wc = new WebClient();
            wc.OpenReadCompleted += new OpenReadCompletedEventHandler(wc_OpenReadCompleted);
            wc.OpenReadAsync(new Uri(
cls.xap, UriKind.Relative));

        }

        void wc_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e)
        {
            UIElement element = GetUIElementFromXap(e,
cls.applicationName, cls.control);
            LayoutRoot.Children.Add(element);
        }

        private static UIElement GetUIElementFromXap(OpenReadCompletedEventArgs e, string applicationName, string control)
        {
            StreamResourceInfo resourceInfoDLL = Application.GetResourceStream(new StreamResourceInfo(e.Result, null),
                                                 new Uri(string.Format("{0}.dll", applicationName), UriKind.Relative));
            AssemblyPart assemblyPart = new AssemblyPart();
            Assembly assembly = assemblyPart.Load(resourceInfoDLL.Stream);
            UIElement element = assembly.CreateInstance(string.Format("{0}.{1}", applicationName, control)) as UIElement;
            return element;
        }

    }

Once we have set up MEF in our application we can simply go ahead and give it a run.

XAPS On demand in Silverlight

Hence our Plugin gets loaded successfully.

Happy Coding.