从插件程序集中将WPF UI加载到MVVM应用程序中
我正在开发一个使用插件架构来扩展其function的应用程序。 从插件加载WPF UI的最佳方法是什么?
我将有一个列出所有可用插件的列表框。 选择插件后,插件中定义的WPF UI应显示在ContentControl
。 我想到的选项包括:
- 需要创建实现特定接口的
UserControl
。 我认为这将使插件创建变得容易。 实现一个界面,你很高兴。 我对这个方法的问题是如何将UserControl
动态加载到ContentControl
。 此外,由于我使用的是MVVM设计模式,因此DataTemplate
似乎比UserControl
。 - 允许从插件加载
DataTemplate
。 我相信这需要插件包含一个名为某种方式的XAML文件。 我的应用程序会将DataTemplate
读入我的资源字典中,如此问题所示。 我已经看到了很多类似的问题,除了它们通常只需要加载一个额外的预定义程序集来从中获取DataTemplates
。 此问题需要搜索任何数量的未知程序集以查找DataTemplates
。
如果我选择第二个选项,我想我可以选择DataTemplate
与此答案描述的方式类似。
您认为哪种方法更好? 或者你有更好的方法来实现这一目标吗?
我做了类似于DataTemplates
提到的类似的事情。 我使用MEF加载插件,然后在启动时加载了一个带有ViewModel
和View
引用的Dictionary
。 该插件使用3个主要组件构建。
IBasePlugin.cs
这个简单的界面允许我们为插件创建一个骨架。 这将只包含非常基础,因为这是我们将使用MEF
将插件Import
主应用程序的方法。
public interface IBasePlugin { WorkspaceViewModel ViewModel { get; } ResourceDictionary View{ get; } }
Plugin.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... }
View.xaml
这是一个DataTemplate
包含对插件View
和ViewModel
的引用。 这是我们将用于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); }
一旦将Dictinoary
和ViewModel
从我们的插件加载到我们的应用程序中,我们就可以使用例如TabControl
来显示该集合。
我在这里也给出了类似的答案以及一些您可能感兴趣的其他细节。
听起来你正在寻找的东西已经用Prism完成了。 您可以定义区域,然后在运行时加载模块,这些模块可能有也可能没有这些区域的视图。 如果所有应用程序都是根据Prism派生的模块化概念构建的,那么这种方法很有效。 还有其他人,但Prism对此进行了相当广泛的处理。
我对富士使用了类似的方法。 唯一的区别是我将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
。
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)) { } }