2015-02-12 36 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