Silverlight 4, MEF and MVVM: MEFModules, Dynamic XAP Loading and Navigation Applications

In the last example I’ve implemented¬†a new “MEF Module” organized with a MVVM approach and using Prism Event Aggregator to exchange messages.

This new module could be used to partititon the application in several XAPs composed dynamically at run-time using MEF, the new DeploymentCatalog and the Navigation features available in Silverlight.

To start I’ve created a new “Silverlight Navigation Application” which produces a complete application with ready-to-use navigation and localization.

Then I’ve inserted a new “Silverlight User Control” inside the “Views” folder and named it “MEFModuleContainer“: this one is called by the navigation framework and is responsible to load dynamically the module using the MEF “DeploymentCatalog“.

This is the XAML of the container:

<StackPanel x:Name="ContentStackPanel">
    <TextBlock x:Name="HeaderText" Style="{StaticResource HeaderTextStyle}"
               Text="MEF Module container"/>
    <TextBlock x:Name="ContentText" Style="{StaticResource ContentTextStyle}"
               Text="MEF content"/>
    <ItemsControl x:Name="content"/>
</StackPanel>

Our “MEF Module” is hosted in a “ItemsControl” using the “Items” property:

public MEFModuleContainer()
{
    InitializeComponent();

    CompositionInitializer.SatisfyImports(this);

    CatalogService.AddXap("MEFModule.xap");
}

[Import]
public IDeploymentCatalogService CatalogService { get; set; }

[ImportMany(AllowRecomposition = true)]
public Lazy<UserControl>[] MEFModuleList { get; set; }

#region IPartImportsSatisfiedNotification Members

public void OnImportsSatisfied()
{
    MEFModuleList.ToList()
        .ForEach(module=>
            content.Items.Add(module.Value)
        );
}

#endregion

In order to use the DeploymentCatalog, it’s necessary to define an interface IDeploymentCatalogService (read this post by Glenn Block for more information about it):

public interface IDeploymentCatalogService
{
    void AddXap(string uri, Action<AsyncCompletedEventArgs> completedAction = null);
    void RemoveXap(string uri);
}

and implement it in the class “DeploymentCatalogService“:

[Export(typeof(IDeploymentCatalogService))]
public class DeploymentCatalogService : IDeploymentCatalogService
{
    private static AggregateCatalog _aggregateCatalog;

    Dictionary<string, DeploymentCatalog> _catalogs;

    public DeploymentCatalogService()
    {
        _catalogs = new Dictionary<string, DeploymentCatalog>();
    }

    public static void Initialize()
    {
        _aggregateCatalog = new AggregateCatalog();
        _aggregateCatalog.Catalogs.Add(new DeploymentCatalog());
        CompositionHost.Initialize(_aggregateCatalog);
    }

    public void AddXap(string uri, Action<AsyncCompletedEventArgs> completedAction = null )
    {
        DeploymentCatalog catalog;
        if (!_catalogs.TryGetValue(uri, out catalog))
        {
            catalog = new DeploymentCatalog(uri);
            if (completedAction != null)
                catalog.DownloadCompleted += (s, e) => completedAction(e);
            else
                catalog.DownloadCompleted += catalog_DownloadCompleted;

            catalog.DownloadAsync();
            _catalogs[uri] = catalog;
            _aggregateCatalog.Catalogs.Add(catalog);
        }
    }

    void catalog_DownloadCompleted(object sender, AsyncCompletedEventArgs e)
    {
        if (e.Error != null)
            throw e.Error;
    }

    public void RemoveXap(string uri)
    {
        DeploymentCatalog catalog;
        if (_catalogs.TryGetValue(uri, out catalog))
        {
            _aggregateCatalog.Catalogs.Remove(catalog);
        }
    }
}

The DeploymentCatalogService is initialized during the Application startup in this way:

private void Application_Startup(object sender, StartupEventArgs e)
{
    //Initialize the DeploymentCatalogService for MEF
    DeploymentCatalogService.Initialize();
    this.RootVisual = new MainPage();
}

When the user clicks on the Navigation bar, the container is loaded and filled with the content of the external XAP, all managed by MEF:

So we have a Navigation application which can dynamically load external “MEFModules” organized with a MVVM approach, contained in external XAPs and using an Event Aggregator to exchange messages, all managed by MEF.

This application can be easily extended inserting WCF RIA Services and/or Blend sample data for the design mode.

As usually the sample code is available for download here and will be soon available in the CodePlex project “MEF MVVM” – http://mefmvvm.codeplex.com.

Happy Silverlighting!