12

我正在开发一个使用插件架构来扩展其功能的应用程序。从插件加载 WPF UI 的最佳方式是什么?

我将有一个列表框列出所有可用的插件。选择插件后,插件中定义的 WPF UI 应以ContentControl. 我想到的选项包括:

  • 需要UserControl创建一个实现特定接口的。我认为这将使插件创建变得容易。实现一个接口,你就可以开始了。我对这种方法的问题是如何UserControlContentControl. 此外,由于我使用的是 MVVM 设计模式,因此 a 似乎DataTemplate优于UserControl.
  • 允许DataTemplate从插件加载。我相信这需要插件包含一个以某种方式命名的 XAML 文件。我的应用程序会读DataTemplate入我的资源字典,如这个问题所示。 我见过很多类似的问题,除了它们通常只需要加载一个额外的预定义程序集即可DataTemplates。这个问题需要搜索任意数量的未知程序集DataTemplates

如果我选择第二个选项,我想我可以选择DataTemplate类似于此答案描述的方式。

你认为哪种方法更好?或者你有更好的方法来完成这个吗?

4

3 回答 3

13

我做了类似提到的类似的事情DataTemplates。我使用MEF加载插件,然后在启动时加载了对andDictionary的引用。该插件使用 3 个主要组件构建。ViewModelView

IBasePlugin.cs

这个简单的界面允许我们为插件创建一个骨架。这将只包含非常基础的内容,因为这是我们将Import使用MEF.

public interface IBasePlugin
{
    WorkspaceViewModel ViewModel { get; }
    ResourceDictionary View{ get; }
}

插件.cs

下一部分是Plugin.cs文件。它包含我们插件的所有属性,以及所有必要的参考;比如我们的View& ViewModel

[Export(typeof(IBasePlugin))]
public class Plugin : IBasePlugin
{
    [Import]
    private MyPluginViewModel _viewModel { get; set; }
    private ResourceDictionary _viewDictionary = new ResourceDictionary();

    [ImportingConstructor]
    public Plugin()
    {
        // First we need to set up the View components.
        _viewDictionary.Source =
            new Uri("/Extension.MyPlugin;component/View.xaml",
            UriKind.RelativeOrAbsolute);
    }

    ....Properties...

}

查看.xaml

这是一个DataTemplate包含对插件的引用ViewViewModel. 这就是我们将用于Plugin.cs加载到主应用程序中的内容,以便应用程序WPF知道如何将所有内容绑定在一起。

<DataTemplate DataType="{x:Type vm:MyPluginViewModel}">
    <vw:MyPluginView/>

然后,我们使用MEF加载所有插件,将它们提供给我们ViewModel负责处理插件的工作区,并将它们存储在一个ObservableCollection用于显示所有可用插件的容器中。

我们用来加载插件的代码看起来像这样。

var plugins = Plugins.OrderBy(p => p.Value.ViewModel.HeaderText);
foreach (var app in plugins)
{
    // Take the View from the Plugin and Merge it with,
    // our Applications Resource Dictionary.
    Application.Current.Resources.MergedDictionaries.Add(app.Value.View)

    // THen add the ViewModel of our plugin to our collection of ViewModels.
    var vm = app.Value.ViewModel;
    Workspaces.Add(vm);
}

一旦DictinoaryViewModel都从我们的插件加载到我们的应用程序中,我们可以使用例如 a 来显示集合TabControl

<TabControl ItemsSource="{Binding Workspaces}"/>

我在这里也给出了类似的答案以及一些您可能会感兴趣的其他细节。

于 2012-08-30T20:15:52.473 回答
2

我使用与富士类似的方法。唯一的区别是我相互独立地导出ViewModel和,ResourceDictionary所以它们仍然是松散耦合的。

[Export(typeof(IPlugin)]//i use metadata too
public class Viewmodel1 {}

[Export(typeof(IPlugin)]//i use metadata too
public class Viewmodel2 {}

[ResourceDictionaryExport]
public partial class MyResourceDictionary 
{
    public MyResourceDictionary ()
    {
        InitializeComponent();
    }
}

在我的插件应用程序中,我添加了所有ResourceDictionaries.

应用程序.xaml.cs

 [ImportMany("Resourcen", typeof(ResourceDictionary))]
 private IEnumerable<ResourceDictionary> _importResourcen;

 foreach (var resourceDictionary in _importResourcen)
 {
     this.Resources.MergedDictionaries.Add(resourceDictionary);
 }

为了完整起见

[MetadataAttribute]
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public class ResourceDictionaryExportAttribute : ExportAttribute
{
    public ResourceDictionaryExportAttribute() : base("Resourcen", typeof(ResourceDictionary))
    {

    }
}
于 2012-08-31T08:06:18.923 回答
2

听起来您正在寻找的东西已经用 Prism 完成了。您定义区域,然后在运行时加载可能有也可能没有这些区域的视图的模块。如果您的所有应用程序都是针对从 Prism 派生的模块化概念构建的,则此方法有效。还有其他的,但是 Prism 已经非常广泛地处理了这个问题。

于 2012-08-30T20:16:25.873 回答