如何动态加载UserControls到TabControl

时间:2015-02-12 07:48:13

标签: c# wpf binding

我有几个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>

现在,我更改了程序,以便动态加载每个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的UserControls。类似的东西:

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

但是,我似乎无法对控件进行绑定。

2 个答案:

答案 0 :(得分:1)

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

一个。所有的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 
        }
    }
}

湾在我的主机应用程序中,扫描所有的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);
                }
            }
        }
    }
}

℃。然后只需加载控件

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 :(得分:1)

您可以尝试这样,因为您的用户控制在另一个DLL中。创建一个包含两个界面的新项目

public interface IMetaData
{
    string Name
    {
        get;
    }
}

public interface IView
{ }

并将此项目引用到您的Main应用程序,data1.dll将IView添加到您的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);
    }
}

}

在object类型

的视图模型中创建一个属性
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>


 像这样

访问viewmodel中的usercontrol
var pluginContainer = Plugins.First(x => x.Metadata.Name == "uc1");
if (pluginContainer != null)
{
  PlugIn=pluginContainer.Value;
 OnPropertyChanged("PlugIn"); 
}