So far, we've been using the default composition container and the default catalog that it uses. In this workshop, we're going to take control of the catalogs being used by the default composition container. We'll configure two DeploymentCatalog
catalogs to use parts from—one to find parts in the current XAP file, and another that will download another XAP file and find parts in it. We'll then tie these together using an AggregateCatalog
catalog, and associate them with the default container.
IPersonPart
interface as their contract across multiple XAP files. The first step is to create a new “common” assembly that will contain the interface, which both XAP files can reference. Add a new project to the solution named Chapter14Sample.Common
, using the Silverlight Class Library project template.IPersonPart
interface from the Chapter14Sample
project to this new Chapter14Sample.Common
project.Chapter14Sample
project
needs a reference to the Chapter14Sample.Common
project. Add this now. If you compile and run your project, it should run as per the previous workshop.using
statement to the top of the MainPage.xaml.cs
file:
using System.ComponentModel.Composition.Hosting;
MainPage
class's constructor:
public MainPage()
{
InitializeComponent();
var aggregateCatalog = new AggregateCatalog();
var catalog1 = new DeploymentCatalog();
aggregateCatalog.Catalogs.Add(catalog1);
CompositionHost.Initialize(aggregateCatalog);
CompositionInitializer.SatisfyImports(this);
var list = new ListBox();
list.ItemsSource = MyImportedPersonParts;
list.DisplayMemberPath = "Name";
LayoutRoot.Children.Add(list);
}
In this code, we've added a DeploymentCatalog
instance to an AggregateCatalog
catalog, and passed the AggregateCatalog
catalog to the CompositionHost
to use. The DeploymentCatalog
catalog is configured to simply look in the current XAP file for any exports. Once again, if you compile and run your project, it should run as per the previous workshop.
DeploymentCatalog
catalog. Add a new project to the solution named Module1
, using the Silverlight Application project template. Deselect the “Add a test page that references the application” check box in the wizard that appears, and click OK.System.ComponentModel.Composition.dll
Chapter14Sample.Common.dll
(as a project reference)YetAnotherPersonPart.cs
.PersonPart
and AnotherPersonPart
classes that you created in the previous workshops. Simply assign another name to the Name
property in its constructor to differentiate it from the other classes.
MainPage
class in the Chapter14Sample
project. We now need to create a new DeploymentCatalog
instance that points to the XAP file of the module that we just created, and add it to the AggregateCatalog
catalog. Add the following code in bold to the MainPage
class's constructor:
public MainPage()
{
InitializeComponent();
var aggregateCatalog = new AggregateCatalog();
var catalog1 = new DeploymentCatalog();
aggregateCatalog.Catalogs.Add(catalog1);
var catalog2 =
new DeploymentCatalog(new Uri("Module1.xap", UriKind.Relative));
catalog2.DownloadAsync();
aggregateCatalog.Catalogs.Add(catalog2);
CompositionHost.Initialize(aggregateCatalog);
CompositionInitializer.SatisfyImports(this);
var list = new ListBox();
list.ItemsSource = MyImportedPersonParts;
list.DisplayMemberPath = "Name";
LayoutRoot.Children.Add(list);
}
MyImportedPersonParts
property to support recomposition. This will allow it to be updated with new parts as they become available (i.e., after the module XAP file has been downloaded).
[ImportMany(AllowRecomposition=true)]
public ObservableCollection<IPersonPart> MyImportedPersonParts { get; set; }
Note how the type was changed from an array to an observable collection. MEF will update this collection as parts become available, and being an observable collection means that the ListBox
control will be aware of the changes and update itself accordingly. This will require the following using
statement to be added to the top of the file:
using System.Collections.ObjectModel;
The final code that you should have for the MainPage
class is as follows:
using System;
using System.Collections.ObjectModel;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using System.Windows.Controls;
namespace Chapter14Sample
{
public partial class MainPage : UserControl
{
[ImportMany(AllowRecomposition=true)]
public ObservableCollection<IPersonPart> MyImportedPersonParts
{ get; set; }
public MainPage()
{
InitializeComponent();
var aggregateCatalog = new AggregateCatalog();
var catalog1 = new DeploymentCatalog();
aggregateCatalog.Catalogs.Add(catalog1);
var catalog2 =
new DeploymentCatalog(new Uri("Module1.xap", UriKind.Relative));
catalog2.DownloadAsync();
aggregateCatalog.Catalogs.Add(catalog2);
CompositionHost.Initialize(aggregateCatalog);
CompositionInitializer.SatisfyImports(this);
var list = new ListBox();
list.ItemsSource = MyImportedPersonParts;
list.DisplayMemberPath = "Name";
LayoutRoot.Children.Add(list);
}
}
}
Note The Chapter14Sample.Common.dll
assembly will be compiled into both the Chapter14Sample.xap
and Module1.xap
files. You can use Silverlight's assembly caching feature, covered in Chapter 17, to download the assembly independently from both XAP files, resulting in only one copy needing to be downloaded to the client. This is rather important when you have many shared libraries between the “shell application” and the modules, especially when the shared libraries are sizable.