Reputation: 127
I have several UserControls (in different DLLs) that I am inserting as TabItems into a TabControl of my main Application (EXE). This is the XAML I use to load it and add it to the TabControl:
<TabControl x:Name="mainTabControl">
<TabItem Style="{StaticResource VertAlign}" Header="firstDll">
<view1:ControlOfTheFirstDll />
</TabItem>
<TabItem Style="{StaticResource VertAlign}" Header="secondDll">
<view2:ControlOfTheSecondDll />
</TabItem>
</TabControl>
Now, I changed my program so that I load each DLL (Data1.dll
, Data2.dll
) dynamically. With the Startup of my program, an instance of the following class Plugin
is created and can be accessed from within my ViewModel.
class Plugin
{
[ImportMany]
public IEnumerable<IDataProvider> DataProvider { get; set; }
public Plugin()
{
try
{
AggregateCatalog aggregatecatalogue = new AggregateCatalog();
aggregatecatalogue.Catalogs.Add(new AssemblyCatalog(Assembly.LoadFile("...\\Data1.dll")));
aggregatecatalogue.Catalogs.Add(new AssemblyCatalog(Assembly.LoadFile("...\\Data2.dll")));
aggregatecatalogue.Catalogs.Add(new AssemblyCatalog(Assembly.GetAssembly(typeof(IDataProvider))));
CompositionContainer container = new CompositionContainer(aggregatecatalogue);
container.ComposeParts(this);
}
catch (FileNotFoundException fnfex)
{
Console.WriteLine(fnfex.Message);
}
catch (CompositionException cex)
{
Console.WriteLine(cex.Message);
}
}
}
Somehow, I need to load the UserControls, which are based in Data1.dll
and Data2.dll
, dynamically. Something like:
<TabControl x:Name="mainTabControl">
<TabItem Style="{StaticResource VertAlign}" Header="firstDll">
<ContentControl Content="{Binding PluginInstance.Control.???}" />
</TabItem>
<TabItem Style="{StaticResource VertAlign}" Header="secondDll">
<ContentControl Content="{Binding PluginInstance.Control.???}" />
</TabItem>
</TabControl>
However, I can't seem to get the binding on the control working.
Upvotes: 1
Views: 1035
Reputation: 669
i just recently created something similar but it does not exactly match your implementation as i'm doing it through simple reflection (and the app is not MVVM at all). here it is, i hope you can pick up something useful.
a. all the the dlls have a class that implement IPlugin interface
public interface IPlugin
{
string Name { get; }
UserControl PluginControl { get; } //or a tab item
}
//a sample dll would have
public class SamplePlugin : IPlugin
{
public string Name { get { return "sample"; } }
public PluginControl
{
get {
return AnInstanceOfControlHere; //create this somewhere
}
}
}
b. in my host application, scan all the dlls and reflect all classes that implements IPlugin, then instantiate.
List<IPlugin> _LISTOFALLPLUGINS = new List<IPlugin>();
public void LoadPlugins()
{
if (Directory.Exists(YOURFOLDERPATH_HERE))
{
string[] pluginfiles = Directory.GetFiles(YOURFOLDERPATH_HERE, "*.dll", SearchOption.AllDirectories);
var pluginbasetype = typeof(IPlugin);
foreach (string pluginpath in pluginfiles)
{
Assembly assembly = Assembly.LoadFile(pluginpath);
List<Type> plugins = assembly.GetTypes().Where(t => pluginbasetype.IsAssignableFrom(t)).ToList();
foreach (Type plugintype in plugins)
{
if (!plugintype.IsAbstract)
{
IPlugin plugin = assembly.CreateInstance(plugintype.FullName) as IPlugin;
_LISTOFALLPLUGINS.Add(plugin);
}
}
}
}
}
c. then simply load the controls
public void ShowPlugins()
{
foreach(IPlugin plugin in _LISTOFALLPLUGINS)
{
TabItem ti = new TabItem();
ti.Header = plugin.Name;
UserControl uc = app.PluginControl;
if (uc != null)
{
ti.Content = uc;
}
MYTAB.Items.Add(ti);
}
}
note: this is probably not what you're looking for especially with regards to just using xaml, but i find that this is quite a simple plugin-style components that's easy to implement. aside from UI, you can incorporate complex operations as well in the plugin dll. this was based on something i read many years back, though. can't find the link.
*if not you, then i do hope someone else can find it useful :)
Upvotes: 1
Reputation: 1230
You can try like this since your usercontrol in another dll. create a new project with two interface in it
public interface IMetaData
{
string Name
{
get;
}
}
public interface IView
{ }
and reference this project to your Main application and data1.dll add IView to your usercontrol like this.
[Export(typeof(IView)), PartCreationPolicy(System.ComponentModel.Composition.CreationPolicy.Any)]
[ExportMetadata("Name", "uc1")]
public partial class myusercontrol : UserControl,IView
{
public ServiceContractList()
{
InitializeComponent();
}
}
and in your view model
class Plugin
{
[ImportMany]
public IEnumerable<IDataProvider> DataProvider { get; set; }
//add this
[ImportMany(typeof(IView), AllowRecomposition = true)]
public IEnumerable<Lazy<IView,IMetaData>> Plugins
{
get;
set;
}
public Plugin()
{
try
{
AggregateCatalog aggregatecatalogue = new AggregateCatalog();
aggregatecatalogue.Catalogs.Add(new AssemblyCatalog(Assembly.LoadFile("...\\Data1.dll")));
aggregatecatalogue.Catalogs.Add(new AssemblyCatalog(Assembly.LoadFile("...\\Data2.dll")));
aggregatecatalogue.Catalogs.Add(new AssemblyCatalog(Assembly.GetAssembly(typeof(IDataProvider))));
CompositionContainer container = new CompositionContainer(aggregatecatalogue);
container.ComposeParts(this);
}
catch (FileNotFoundException fnfex)
{
Console.WriteLine(fnfex.Message);
}
catch (CompositionException cex)
{
Console.WriteLine(cex.Message);
}
}
}
create a property in the view model of type object
private object _PlugIn;
public object PlugIn
{
get
{
return _PlugIn;
}
set
{
if (value != _PlugIn)
{
_PlugIn = value;
OnPropertyChanged("PlugIn");
}
}
}
bind this to your ContentControl
<TabControl x:Name="mainTabControl">
<TabItem Style="{StaticResource VertAlign}" Header="firstDll">
<ContentControl Content="{Binding PlugIn}" />
</TabItem>
access your usercontrol in the viewmodel like this
var pluginContainer = Plugins.First(x => x.Metadata.Name == "uc1");
if (pluginContainer != null)
{
PlugIn=pluginContainer.Value;
OnPropertyChanged("PlugIn");
}
Upvotes: 1