C# 如何将UserControls动态加载到TabControl

C# 如何将UserControls动态加载到TabControl,c#,wpf,binding,C#,Wpf,Binding,我有几个UserControls(在不同的DLL中)作为TabItems插入到我的主应用程序(EXE)的TabControl中。这是我用来加载它并将其添加到TabControl的XAML: <TabControl x:Name="mainTabControl"> <TabItem Style="{StaticResource VertAlign}" Header="firstDll"> <view1:ControlOfTheFirstDll

我有几个UserControls(在不同的DLL中)作为TabItems插入到我的主应用程序(EXE)的TabControl中。这是我用来加载它并将其添加到TabControl的XAML:

<TabControl x:Name="mainTabControl">
    <TabItem Style="{StaticResource VertAlign}" Header="firstDll">
        <view1:ControlOfTheFirstDll />
    </TabItem>
    <TabItem Style="{StaticResource VertAlign}" Header="secondDll">
        <view2:ControlOfTheSecondDll />
    </TabItem>
</TabControl>
不知何故,我需要动态加载UserControls,它们基于
Data1.dll
Data2.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>


但是,我似乎无法使控件上的绑定正常工作。

我最近刚刚创建了类似的东西,但它与您的实现并不完全匹配,因为我正在通过简单的反射来实现它(而且该应用程序根本不是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);
                }
            }
        }
    }
}
注意:这可能不是您想要的,特别是关于仅使用xaml, 但我发现这是一个非常简单的插件式组件,很容易实现。 除了UI,您还可以在插件dll中合并复杂的操作。 不过,这是基于我多年前读到的东西。找不到链接


*如果不是你,那么我真的希望其他人能发现它有用:)

你可以这样尝试,因为你的用户控件在另一个dll中。创建一个包含两个接口的新项目

public interface IMetaData
{
    string Name
    {
        get;
    }
}

public interface IView
{ }
然后将此项目引用到主应用程序,并将data1.dll添加到usercontrol,如下所示

[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);
    }
}
将此绑定到ContentControl

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

:我正在做一些与您的解决方案类似的事情,但有一个问题,即如果我想在同一窗口中显示两个相同类型的控件,我只会得到一个实例。是否有可能创建同一控件的两个实例?
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");
            }
        }
    }
 <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"); 
}