Sofa New Release, AvalonDock and MEF
This article introduces the new Sofa release and impacts on using MEF:
- Sofa can now be embedded in a project in a very light and easy way, without the parameters file.
- New methods have been added and allow a better integration with MEF. Update starts at the "Sofa and MEF" paragraph.
Download Package
The download package is a Sofa enabled application. This application includes 2 components. The 1st one is the WebBrowser
and the 2nd is the SofaExplorer
used as example in this article. This application is one of the 4 that are provided with the full package available in the Sofa web site.
What is AvalonDock ?
AvalonDock is a WPF controls library which can be used to create a docking layout system like that is present in Visual Studio.
It supports fly-out panes, floating windows, multiple docking manager in same window, styles and themes and it can host WinForms controls.
See more at the AvalonDock web site.
What is Sofa?
Sofa wraps AvalonDock and provides:
- Easier programming
- Enhanced features
- Additional features
One of those features are built-in functionalities that make Sofa MEF-compatible.
See more at the Sofa web site.
What Problems does MEF Solve?
- MEF presents a simple solution for the runtime extensibility problem.
- MEF provides a standard way for the host application to expose itself and consume external extensions.
- MEF offers a set of discovery approaches for your application to locate and load available extensions.
- MEF allows tagging extensions with additional metadata which facilitates rich querying and filtering.
See more at the MEF web site.
There is a symmetry between the docking layout of Sofa/Avalon and the MEF component architecture: MEF will allow integration of external components that will easily be represented as tabbed or split windows.
The MEF XFileExplorer Example
The example below is based on the MEF FileExplorer
which can be found in the MEF preview download (MEF\MEF2_Preview2\Samples\CS\XFileExplorer) and has been "translated" into a set of Sofa components.
It is a basic file explorer with its treeview
, its contents pane and 3 additional functionalities, a size pane showing a graphical representation of file sizes, a preview pane and favorites.
It is made of a main component named Shell and one "sub-component" for each functionality.
Once the application is translated into Sofa components and runs in the Sofa container, its appearance is not that different. You can find it as a Component of the SofaExplorer
example in the download file.
But the user can now customize it:
Another additional feature in the XFileExplorer
is now a set of Sofa components and it becomes easy to manage many instances of each set using Sofa perspectives (top left menu).
A default filepath is associated to each perspective and allows managing filepaths favorites.
The main goal of this documentation is however not to demonstrate hosting an application as a Sofa component can easily provide additional functionalities: It is to show how MEF concepts and Sofa/AvalonDock concepts fits to each other and are easy to implement.
Micro MEF Tutorial
The way MEF works is very simple: MEF components, called Composable Parts, declare they are composable parts:
[Export("Microsoft.Samples.XFileExplorer.FileExplorerViewContract", typeof(UserControl))]
public partial class PreviewView : UserControl
{
...
Then, the host application registers composable parts using the CompositionContainer.ComposePart()
method: In this example, all Composable Parts that are in the executing assembly will be registered:
private void Compose()
{
var catalog = new AssemblyCatalog(System.Reflection.Assembly.GetExecutingAssembly());
var container = new CompositionContainer(catalog);
container.ComposeParts(this);
}
The XFileExplorer
example uses a different object, the CompositionBatch
, but basically it is the same work.
At last, the hosting application can retrieve composable parts: For instance, in the XFileExplorer
, the 2 following lines will make the _views
object automatically loaded by MEF at run time.
[ImportMany("Microsoft.Samples.XFileExplorer.FileExplorerViewContract",
AllowRecomposition=true)]
private Lazy<UserControl, IFileExplorerViewMetadata>[] _views = null;
Sofa and MEF
When the MEF hosting application becomes a Sofa component, it is no longer an application but a UserControl
. And it loses its App.xaml.cs file where the Compose
method is usually stored.
This piece of code has to be moved to the main component. It is Shell.xaml.cs in our example.
Then Sofa components (i.e., MEF composable parts) must be loaded in AvalonDock:
The first step is to register them. The usual way is to use a CmpModel
bean:
private void Button_Click(object sender, RoutedEventArgs e)
{
CmpModel cmpModel = new CmpModel();
cmpModel.UserControl = new BaseControl();
...
cmpModel.Label = productName;
cmpModel.ProductName = productName;
sofaContainer.RegisterComponentByModel(cmpModel);
...
}
Using MEF is easier:
- The
Export
statement of a Component:
[Export("Sofa.Examples.SofaExplorer.XFileExplorer_ViewContract",
typeof(UserControl))]
[ExportMetadata("ProductName", "Sofa_XFileExplorer_StatusView")]
[ExportMetadata("AvalonContentType", "DockableContent")]
[ExportMetadata("AvalonPaneGroup", "RightSide")]
[ExportMetadata("IfExists_DefaultValue", "DoNotUseIt")]
[ExportMetadata("ImageURI", "")]
[ExportMetadata("Label", "Status")]
[ExportMetadata("DockId", 0)]
[ExportMetadata("Hidden", "False")]
public partial class StatusView : UserControl
{
...
- The
import
statement in the XFileExplorer
:
[ImportMany("Sofa.Examples.SofaExplorer.XFileExplorer_ViewContract",
AllowRecomposition = true)]
private Lazy<UserControl, ISofaComponent_Metadata>[] _views = null;
- The components registration:The
RegisterComponent
method accepts the Lazy<UserControl, ISofaComponent_Metadata>
as parameter:
public void OnImportSatisfied()
{
foreach (var view in _views)
{
shellSofacontainer.RegisterComponent(view);
}
}
At last, Components can be loaded as usual. There are 2 ways:
- Open a perspective that only defines places and then open each Component that will find its place in the perspective
- Open a perspective that defines places and components: Opening the perspective will automatically open components. This is the used way in the
SofaExplorer
.