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