I have tried the following very good tutorial https://www.eidias.com/blog/2013/7/26/plugins-in-wpf-mvvm-with-mef#cm-249 to migrate to MEF2 but for some reason the assemblies are not shown in the catalog. From MEF2 I wanted to use the API Configuration (RegistrationBuilder class) (here an example: https://stefanhenneken.wordpress.com/2013/01/21/mef-teil-11-neuerungen-unter-net-4-5/ ), maybe somebody has an idea how to apply MEF2 correctly to the tutorial. Thank you very much.
here the overview of the solution:
In the MainViewModel.cs I don't know yet how to integrate the imports into the RegistrationBuilder
.Can you check the rest of the code? Thanks.
namespace WPF_MEF_App
{
public class MainWindowModel : NotifyModelBase
{
public ICommand ImportPluginCommand { get; protected set; }
private IView PluginViewVar;
[Import(typeof(IView), AllowRecomposition = true, AllowDefault = true)]
public IView PluginView
{
get { return PluginViewVar; }
set{ PluginViewVar = value; NotifyChangedThis();}
}
[ImportMany(typeof(IView), AllowRecomposition = true)]
public IEnumerable<Lazy<IView>> Plugins;
private AggregateCatalog catalog;
private CompositionContainer container;
public MainWindowModel()
{
ImportPluginCommand = new DelegateCommand(ImportPluginExecute);
RegistrationBuilder builder = new RegistrationBuilder();
builder.ForType<PluginSecondScreen>()
.Export<IView>(eb =>
{
eb.AddMetadata("Name", "PluginSecond");
})
.SetCreationPolicy(CreationPolicy.Any);
//.ImportProperties(pi => pi.Name == "IView",
// (pi, ib) => ib.AllowRecomposition());
builder.ForType<CalculatorScreen>()
.Export<IView>(eb =>
{
eb.AddMetadata("Name", "CalculatorScreen");
})
.SetCreationPolicy(CreationPolicy.Any);
//.ImportProperties(pi => pi.Name == "IView",
// (pi, ib) => ib.AllowRecomposition());
catalog = new AggregateCatalog();
string pluginsPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
catalog.Catalogs.Add(new DirectoryCatalog(pluginsPath, "Plugin*.dll"));
catalog.Catalogs.Add(new AssemblyCatalog(Assembly.GetExecutingAssembly(), builder));
//also we add to a search path a subdirectory plugins
pluginsPath = Path.Combine(pluginsPath, "plugins");
if (!Directory.Exists(pluginsPath))
Directory.CreateDirectory(pluginsPath);
catalog.Catalogs.Add(new DirectoryCatalog(pluginsPath, "Plugin*.dll"));
//Create the CompositionContainer with the parts in the catalog.
container = new CompositionContainer(catalog);
}
private void ImportPluginExecute()
{
//refresh catalog for any changes in plugins
//catalog.Refresh();
//Fill the imports of this object
//finds imports and fills in all preperties decorated
//with Import attribute in this instance
container.ComposeParts(this);
//another option
//container.SatisfyImportsOnce(this);
}
}
}
Here are the two plugins:
I have already commented the exports here, because they are no longer needed for RegistrationBuilder
.
I checked your attempt. A few points that need improvement.
ExportFactory<TImport>
(never pass around the container).get
andset
to the definitions ofPluginView
andPlugins
.Import
attribute from all properties in theMainWindowModel
.IView
and a single import (cardinality). You should either import a collection of concrete types, register only a single concrete type or introduce a specialized interface for each concrete type (e.g.PluginSecondScreen
andICalculatorScreen
) where each interface inherits the shared interface (e.g.IView
).CompositionContainer
after you are done with initialization.SetCreationPolicy(CreationPolicy.Any)
is redundant asCreationPolicy.Any
is the default value which usually defaults toCreationPolicy.Shared
.string
literals when using class or class member or type names. Usenameof
instead:ImportProperties(pi => pi.Name == "Plugins")
should be:
ImportProperties(pi => pi.Name == nameof(MainWindowModel.Plugins)
. This makes refactoring a lot easier.MainWindowModel.cs
Interfaces
IView
and specializations in order to create unique types:Interface implementations:
Initialize the application from App.xaml.cs using the
Application.Startup
event handler: