2015-02-12 40 views
1

我有几个用户控件(在不同的DLL中),我作为TabItems插入到我的主应用程序(EXE)的TabControl中。这是我用来加载它,并把它添加到TabControl的XAML:如何动态加载UserControls到TabControl

<TabControl x:Name="mainTabControl"> 
    <TabItem Style="{StaticResource VertAlign}" Header="firstDll"> 
     <view1:ControlOfTheFirstDll /> 
    </TabItem> 
    <TabItem Style="{StaticResource VertAlign}" Header="secondDll"> 
     <view2:ControlOfTheSecondDll /> 
    </TabItem> 
</TabControl> 

现在,我改变了我的计划,使我动态加载每个DLL(Data1.dllData2.dll)。随着我的程序启动,创建了以下类Plugin的一个实例,可以从我的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); 
     } 
    } 
} 

不知何故,我需要加载用户控件,这是基于在Data1.dllData2.dll,动态。例如:

<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> 

但是,我似乎无法获得控制工作的绑定。

回答

1

我刚刚创建了类似的东西,但它并不完全符合您的实现,因为我通过简单的反射来完成它(并且该应用程序根本不是MVVM)。在这里,我希望你能拿起一些有用的东西。

a。所有的dll都有一个实现IPlugin接口的类

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。在我的主机应用程序中,扫描所有dll并反映实现IPlugin的所有类 ,然后实例化。

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。然后只需加载控件

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); 
    } 
} 

注意:这可能不是你要找的内容尤其是关于刚刚使用XAML, 但我觉得这是一个相当简单的插件式组件很容易实现。除了UI之外,你还可以在插件DLL中包含复杂的操作。这是基于我多年前读过的东西,不过。找不到链接。

*如果不是你的话我希望别人能发现它很有用:)

1

你可以尝试这样的,因为在另一个DLL你的用户控件。创建一个新的项目,在它

public interface IMetaData 
{ 
    string Name 
    { 
     get; 
    } 
} 

public interface IView 
{ } 

两种接口,并参考这个项目的主要应用和data1.dll IVIEW添加到您的用户控件这样。

[Export(typeof(IView)), PartCreationPolicy(System.ComponentModel.Composition.CreationPolicy.Any)] 
[ExportMetadata("Name", "uc1")] 
public partial class myusercontrol : UserControl,IView 
{ 
    public ServiceContractList() 
    { 
     InitializeComponent(); 
    } 
} 

,并在您的视图模型

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); 
    } 
} 

}

创建类型的对象视图模型绑定属性

private object _PlugIn; 
    public object PlugIn 
    { 
     get 
     { 
      return _PlugIn; 
     } 
     set 
     { 
      if (value != _PlugIn) 
      { 
       _PlugIn = value; 
       OnPropertyChanged("PlugIn"); 
      } 
     } 
    } 

给你的ContentControl中

<TabControl x:Name="mainTabControl"> 
<TabItem Style="{StaticResource VertAlign}" Header="firstDll"> 
    <ContentControl Content="{Binding PlugIn}" /> 
</TabItem> 


访问您在像这样的

var pluginContainer = Plugins.First(x => x.Metadata.Name == "uc1"); 
if (pluginContainer != null) 
{ 
    PlugIn=pluginContainer.Value; 
OnPropertyChanged("PlugIn"); 
} 
+0

的视图模型用户控件:我做的比你的解决方案类似的东西,但有问题,如果我要显示在同一种类的2个控制同一个窗口,我只得到1个实例。是否有可能创建2个相同控件的实例? – MTR 2015-04-23 08:12:41