从插件程序集中将WPF UI加载到MVVM应用程序中

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

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

  • 需要创建实现特定接口的UserControl 。 我认为这将使插件创建变得容易。 实现一个界面,你很高兴。 我对这个方法的问题是如何将UserControl动态加载到ContentControl 。 此外,由于我使用的是MVVM设计模式,因此DataTemplate似乎比UserControl
  • 允许从插件加载DataTemplate 。 我相信这需要插件包含一个名为某种方式的XAML文件。 我的应用程序会将DataTemplate读入我的资源字典中,如此问题所示。 我已经看到了很多类似的问题,除了它们通常只需要加载一个额外的预定义程序集来从中获取DataTemplates 。 此问题需要搜索任何数量的未知程序集以查找DataTemplates

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

您认为哪种方法更好? 或者你有更好的方法来实现这一目标吗?

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

IBasePlugin.cs

这个简单的界面允许我们为插件创建一个骨架。 这将只包含非常基础,因为这是我们将使用MEF将插件Import主应用程序的方法。

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

Plugin.cs

下一部分是Plugin.cs文件。 它包含我们插件的所有属性,以及所有必要的引用; 比如我们的ViewViewModel

 [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... } 

View.xaml

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

   

然后我们使用MEF加载所有插件,将它们提供给负责处理插件的Workspace ViewModel ,并将它们存储在ObservableCollection ,该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从我们的插件加载到我们的应用程序中,我们就可以使用例如TabControl来显示该集合。

  

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

听起来你正在寻找的东西已经用Prism完成了。 您可以定义区域,然后在运行时加载模块,这些模块可能有也可能没有这些区域的视图。 如果所有应用程序都是根据Prism派生的模块化概念构建的,那么这种方法很有效。 还有其他人,但Prism对此进行了相当广泛的处理。

我对富士使用了类似的方法。 唯一的区别是我将ViewModelResourceDictionary彼此独立地导出,因此它们仍然松散耦合。

 [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

app.xaml.cs

  [ImportMany("Resourcen", typeof(ResourceDictionary))] private IEnumerable _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)) { } }