Using the Managed Extensibility Framework (MEF) to develop modular Silverlight applications
The MEF library appeared relatively recently, but quickly gained popularity among .Net developers for ease of use and efficiency. It allows you to build modular applications with a minimum level of connectivity parts (application) parts. This library includes not only the Dependency Injection container, but also a large amount of infrastructure: many mechanisms for searching for composition elements in assemblies, remote XAP files, a mechanism for marking composition elements using .Net attributes, etc.
There is a version of MEF for Silverlight that differs from the desktop version. We will talk about the features of using MEF for Silverlight applications in this article.
When the method of
There are several features of using this class:
This class is an integral part of the idea of recomposition (re-composition of existing and added parts), carrying out the latter, if there are parts that allow recomposition.
Features of use
Example:Exportfactory
In some situations, you need to create multiple instances of composition elements. For example, if the application allows the user to create multiple copies of documents (composition elements), then this cannot be achieved by conventional means. Take advantage of ExportFactory features.
Example:, the developer takes full responsibility for the lifetime of the created instance. If the instances require freeing up memory, then you can use the standard pattern
Example:
The application must meet the following requirements:
Each theme is located in a separate assembly of a separate XAP file. Themes themselves are stored as resources and retrieved on request.
A class that implements the interface is used as a mechanism for accessing topics
Example:
As soon as the recomposition has taken place, the list of implementations will be updated
Example:
The logic of this code is as follows:
MEF for Silverlight has additional features that are not available even in the desktop version, allowing you to build even more flexible programs.
The recomposition capabilities allow you to change the set of components directly during program operation, which is very important for those cases when dynamic connection / disconnection of functionality, work modules, etc. is required.
Upd: The class was added to the desktop version of the library in MEF 2 Preview 2 .
There is a version of MEF for Silverlight that differs from the desktop version. We will talk about the features of using MEF for Silverlight applications in this article.
Differences from the desktop version
MEF for Siverlight has added several specific classes.- Compositioninitializer
- DeploymentCatalog
- Compositionhost
- Exportfactory
Compositioninitializer
In Silverlight, MEF developers suggest using a classCompositionInitializer(System.ComponentModel.CompositionInitialization.dll library), which allows composition for a specific object by initializing all imports of this object and other dependent entities. This functionality is especially important for Silverlight applications, where the decentralization of application components is especially pronounced. When the method of
SatisfyImports()this class is first called , a global container is created that will be used in all further calls SatisfyImports().SatisfyImportsproduces a composition of all objects that are found in the current assembly and all dependent assemblies (i.e., within the entire XAP file). Objects instantiated during composition will remain in the container until the last one is destroyed, i.e. until the end of the program. There are several features of using this class:
- Classes that invoke a method
SatisfyImports()cannot have an attribute[Export]; - Objects are instantiated only once and stored in the container;
- By default, only the current XAP file is exposed to the composition, which is easy to fix.
public partial class Shell : UserControl
{
public MainPage()
{
ComposeContainer()
}
private void ComposeContainer()
{
CompositionInitializer.SatisfyImports(this);
}
}
* This source code was highlighted with Source Code Highlighter.DeploymentCatalog
This class is designed to dynamically download XAP files, which allows you to further decentralize development, reduce the size of the main XAP file, increase the download speed of the application, etc. XAP files are loaded in asynchronous mode, and the developer can sign up for a notification about the end of the file download and handle errors if they exist.This class is an integral part of the idea of recomposition (re-composition of existing and added parts), carrying out the latter, if there are parts that allow recomposition.
Features of use
DeploymentCatalog:- The browser cache is used when the application is offline
- The class must be used
CompositionHost(see below) - If the same assemblies are present in different XAP files, then he
DeploymentCatalogwill try to add them all to the directory, which can lead to an exception (exception) if recomposition is not allowed. You should exposeCopyLocal=Falseduplicate builds of the entire application or use my extension for VS2010 XapsMinifier - Only assemblies specified in the manifest file are downloaded to the directory from XAP
- In Out of Browser mode, downloadable assemblies are not cached on the file system
private void CancelLoading()
{
catalog.CancelAsync();
}
private void LoadXapFile(string xapPath)
{
DeploymentCatalog catalog = new DeploymentCatalog(xapPath);
catalog.DownloadCompleted += new EventHandler(DownloadCompleted);
catalog.DownloadProgressChanged += new EventHandler(ProgressChanged);
catalog.DownloadAsync();
_aggregateCatalog.Catalogs.Add(catalog);
}
void DownloadCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e)
{
if (e.Error != null)
throw e.Error;
}
void catalog_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
{
int progress = e.ProgressPercentage;
long received = e.BytesReceived;
long total = e.TotalBytesToReceive;
}
* This source code was highlighted with Source Code Highlighter. Compositionhost
This class is the link between directories that contain information on export / import of parts, and a container that contains a composition of parts. This class allows you to override the initial configuration of the application (loading parts only from the current XAP file). It allows you to specify a set of directories, the contents of which will be monitored, and in case of changes (additions / deletions) a recomposition of existing parts will be called.Example:
public partial class Shell : UserControl
{
public MainPage()
{
ComposeContainer()
}
private void ComposeContainer()
{
_aggregateCatalog = new AggregateCatalog(new DeploymentCatalog());
CompositionHost.Initialize(_aggregateCatalog);
CompositionInitializer.SatisfyImports(this);
}
}
* This source code was highlighted with Source Code Highlighter.Exportfactory
In some situations, you need to create multiple instances of composition elements. For example, if the application allows the user to create multiple copies of documents (composition elements), then this cannot be achieved by conventional means. Take advantage of ExportFactory featuresExample:
[Export]
public class DocumentViewModel {
[Import]
public ExportFactory DocumentFactory
{
get;
set;
}
protected List Documents
{
get;
set;
}
public void CreateDocument()
{
Documents.Add(DocumentFactory.CreateExport().Value);
}
}
* This source code was highlighted with Source Code Highlighter. Creating Parts of a Composition Using ExportFactoryDispose(). Example:
[Export]
public class DocumentViewModel : IDisposable
{
private bool isDisposed = false;
[Import]
public ExportFactory DocumentFactory
{
get;
set;
}
private List> ExportLifeTimeContextList
{
get;
set;
}
protected List Documents
{
get;
set;
}
public void CreateDocument()
{
ExportLifetimeContext LifeTimeContext = DocumentFactory.CreateExport();
ExportListLifeTimeContext.Add(LifeTimeContext);
Documents.Add(LifeTimeContext.Value);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
public void Dispose(bool disposing)
{
if (isDisposed)
return;
if (disposing)
{
foreach(IDisposable d in ExportLifeTimeContextList)
d.Dispose();
}
isDisposed = true;
}
}
* This source code was highlighted with Source Code Highlighter. An example of the implementation of the recomposition mechanism
As an example that reflects the simplicity and power of using MEF in Silverlight applications, I chose the implementation of Themes support for application controls.The application must meet the following requirements:
- Support topics
- Have the ability to download third-party XAP files with themes
- Downloading the XAP file should lead to an update of the list of available topics (recomposition)
- The choice of theme should lead to a change in the appearance of controls
Support to
To implement support for themes, I turned to a published project that provides several ready-made sets of topics. Of these projects, I use xaml files with themes for basic controls.Each theme is located in a separate assembly of a separate XAP file. Themes themselves are stored as resources and retrieved on request.
A class that implements the interface is used as a mechanism for accessing topics
IThemeLoader. This class can extract the required resources and contains the name of the topic. Example:
[InheritedExport]
public interface IThemeLoader
{
string Name
{
get;
}
IEnumerable Resources
{
get;
}
}
public class ThemeLoader : ThemeLoaderBase
{
#region IThemeLoader Members
public override string Name
{
get
{
return "Accent";
}
}
public override IEnumerable Resources
{
get
{
yield return LoadResourceDictionary("/SLandMEFdevcamp.AccentTheme;component/Style.xaml");
}
}
#endregion
/*
protected virtual ResourceDictionary LoadResourceDictionary(string uri)
{
return new ResourceDictionary
{
Source = new Uri(uri, UriKind.Relative)
};
}
*/
}
* This source code was highlighted with Source Code Highlighter. The attribute InheritedExportindicates that all implementations of the interface marked with this attribute should be exported.Download third-party XAP files
To support third-party XAP files, on the main form of the application I place the input field for the XAP file address and the download start button. Download is carried out usingDeploymentCabinet, which initiates the recomposition. As soon as the recomposition has taken place, the list of implementations will be updated
IThemeLoaderand a new list of available topics will be displayed on the UI. Example:
private AggregateCatalog _aggregateCatalog = null;
private IEnumerable themesLoaders;
private void ComposeContainer()
{
_aggregateCatalog = new AggregateCatalog(new DeploymentCatalog());
CompositionHost.Initialize(_aggregateCatalog);
CompositionInitializer.SatisfyImports(this);
}
[ImportMany(AllowRecomposition = true)]
public IEnumerable ThemesLoaders
{
get
{
return themesLoaders;
}
set
{
themesLoaders = value;
RaisePropertyChanged("ThemesLoaders");
}
}
private IThemeLoader theme;
public IThemeLoader Theme
{
get
{
return theme;
}
set
{
theme = value;
LoadTheme(value);
RaisePropertyChanged("Theme");
}
}
private void LoadTheme(IThemeLoader themeLoader)
{
if (themeLoader.Resources == null || !themeLoader.Resources.Any())
return;
App.Current.Resources.MergedDictionaries.Clear();
foreach (var resourceDict in themeLoader.Resources)
App.Current.Resources.MergedDictionaries.Add(resourceDict);
}
private void Button_Click(object sender, System.Windows.RoutedEventArgs e)
{
DeploymentCatalog catalog = new DeploymentCatalog(XapUrlTextBox.Text);
catalog.DownloadAsync();
_aggregateCatalog.Catalogs.Add(catalog);
}
* This source code was highlighted with Source Code Highlighter. In this case, XAP files with themes are located in the same folder as the main XAP file, and therefore you can specify only the name of the XAP file without a full url. For example, SLandMEFdevcamp.AccentTheme.xap, SLandMEFdevcamp.Win7Theme.xap. The logic of this code is as follows:
- When creating the form, the container is initialized (
ComposeContainer()) - The user enters the
XapUrlTextBoxaddress of the XAP file in the field and clicks the download button. Downloading the XAP file leads to the addition of a new implementation to the catalogIThemeLoaderand to recomposition of the propertyThemesLoadersthat is associated with the elementListBoxon the form - If the user selects an element
ListBox, a change in the Theme property is generated, which leads to a method callLoadTheme() - The method
LoadTheme()removes all existing resources and adds resources for the theme whose name the user has chosen

Conclusion
MEF has a large number of advantages, including recomposition, the absence of a strictly defined place in which parts of the composition should be registered, etc.MEF for Silverlight has additional features that are not available even in the desktop version, allowing you to build even more flexible programs.
The recomposition capabilities allow you to change the set of components directly during program operation, which is very important for those cases when dynamic connection / disconnection of functionality, work modules, etc. is required.
Project source code
Source code can be downloaded here .Upd: The class was added to the desktop version of the library in MEF 2 Preview 2 .
ExportFactory