I'm trying to make a simple plugin system in which a plugins are dynamically loaded from .dll files on application's startup and show up in the UI.
This Answer seems to be exactly what I'm looking for. It uses MEF to load the plugins. I tried to create a simple project and follow the instructions. My solution has the following structure:
MefTest.SDK
and not plugins)IPlugin.cs
and IPluginViewModel.cs
and the Engine.cs which loads the plugins from the application's directory)MefTest.SDK
)MefTest.SDK
)public interface IPlugin
{
IPluginViewModel ViewModel { get; }
ResourceDictionary View { get; }
string Title { get; }
}
public class Engine
{
[ImportMany]
private IEnumerable<IPlugin> plugins { get; set; }
public async Task<ObservableCollection<IPlugin>> GetPlugins()
{
try
{
var folder = AppDomain.CurrentDomain.BaseDirectory;
var catalog = new AggregateCatalog();
//catalog.Catalogs.Add(new AssemblyCatalog(Assembly.GetExecutingAssembly()));
catalog.Catalogs.Add(new DirectoryCatalog(folder));
var container = new CompositionContainer(catalog);
container.ComposeParts(this);
var result = new ObservableCollection<IPlugin>();
foreach (var p in plugins)
{
result.Add(p);
}
return result;
}
catch (Exception ex)
{
//I get the exception here...
var t = ex;
throw;
}
}
}
[Export(typeof(IPlugin))]
public class Plugin1 : IPlugin
{
private MainViewModel viewModel { get; set; }
public IPluginViewModel ViewModel
{
get { return viewModel; }
}
public ResourceDictionary View
{
get { return viewDictionary; }
}
private ResourceDictionary viewDictionary = new ResourceDictionary();
public string Title
{
get { return "Plugin 1"; }
}
[ImportingConstructor]
public Plugin1()
{
//I get the error here. tried both of these, none of them work
viewDictionary.Source =
// new Uri("pack://application:,,,/MefTest.Plugin1;component/views/main.xaml", UriKind.Absolute);
new Uri("/MefTest.Plugin1;component/views/main.xaml",
UriKind.Relative);
}
public override string ToString()
{
return Title;
}
}
however, I get the error Could not load file or assembly 'MefTest.Plugin1.dll, Culture=neutral' or one of its dependencies. The system cannot find the file specified.
The Main.xaml
file is in MefTest.Plugin1\Views\Main.xaml
folder. Output type of the project is ClassLibrary
and Build Action of the xaml file is Page
.
PS: I tried to reference the plugin directly and add it without the MEF (Plugins.Add(new Plugin3.Plugin3());
) and it still threw the same exception. So I don't think the problem is with the MEF part of the solution.
How can I fix this? Also, is there a better option to this approach?
i do it this way in an xbap application...to retreive distant xaml. Try to do the same with your local resourtce
private ResourceDictionary LoadDictionary(string source)
{
Stream streamInfo = null;
ResourceDictionary dictionary = null;
try
{
streamInfo = DistantManager.Instance.GetResource(source);
if (streamInfo != null)
{
Uri baseUri = DistantManager.Instance.GetUri(source);
dictionary = XamlReader.Load(streamInfo) as ResourceDictionary;
dictionary.Source = baseUri;
}
}
catch (Exception e)
{
BusinessLogger.Manage(e);
return null;
}
return dictionary;
}
something like
Uri baseUri = new Uri(Mysource);
dictionary = XamlReader.Load(XXXX) as ResourceDictionary;
dictionary.Source = baseUri;
but on the other hand I do not understand why you want a ResourceDictionary as your plugin view...? just create the plugin user control ??