为WPF应用程序创建加载项

时间:2016-04-22 09:07:53

标签: c# wpf mvvm prism

我有一个简单的数据库应用程序,用户可以在其中添加或删除人员。此外,应用程序还有一个按钮"向应用程序添加新按钮" 。此应用程序使用Prism框架构建。有两个模块:

  • RibbonControlModule (包含三个按钮 - Add PersonDelete PersonAdd new button to application

  • PersonModule (包含添加和删除人员的逻辑)

我的要求是在运行时添加新按钮。

让我们想象一下情况。我住在华盛顿,我很满意这两个按钮(Add PersonDelete Person)。但我住在新泽西州的朋友鲍勃想要添加新按钮Edit Button,而无需重新编译整个应用程序。也就是说,Bob在可以编辑人物的地方编写dll,然后点击Add new button to application中的RibbonControlModule。之后,EditPerson按钮出现在RibbonControl中,例如ContextMenu。也许EditPerson dll会是另一个Prism模块,我不知道。

也就是说,我的要求是:

  • 可插拔控件
  • 是否可以在不进行补偿的情况下插入控制? (如浏览器中的加载项或扩展名(Classic Notes for Opera)(无需重新启动浏览器即可使用加载项))。
  • 其他程序员可以在不使用我的源代码的情况下开发他们的附加组件
  • 用户插入控件后,应始终将此新控件插入应用程序中。

是否可以使用WPF,MVVM和Prism?我真的很喜欢Prism并且不想否认Prism,但如果"最终证明了手段的合理性,那么我想使用其他技术。

如果有可能,那该怎么办呢?

2 个答案:

答案 0 :(得分:3)

这就是MEF plugin architecture的设计目标。

简而言之,您创建了一个包含插件接口的SDK,并将其作为独立库提供给客户端。然后,客户端的插件将实现此接口,并使用MEF Export属性将其导出,然后主应用程序将导入该属性。

它变得有点棘手的是数据模板,这通常是MVVM的关键组件。简而言之,您的插件需要将其数据模板放在资源字典中,为该字典提供自己的部分类文件,并使用MEF的[Export]属性将其导出。然后您的主应用程序需要导入这些并将它们添加到全局ResourceDictionary的'MergedDictionaries'数组中。这通常与您在单独的传递中导入的所有视图模型类分开完成。实际效果是,您的插件可以在运行时提供视图和视图模型,以及将两者绑定在一起的数据模板,并且它们都可以像将它们静态编译到原始应用程序中一样工作。这也意味着您可以为客户创建插件API,而不会暴露主应用程序的内部。

这是一个非常复杂的话题,考虑到这个问题的一般性如何,如果这个问题没有被标记,我会感到惊讶。如果您想了解更多详情,请告知我们,我们可以将其移至讨论页面。

答案 1 :(得分:3)

您可以使用棱镜中的区域来执行您所描述的内容。您可以在功能区中添加一个命名区域,允许Prism模块在模块首次加载时将新按钮插入该区域,或者在用户单击模块的某个UI中的按钮时将其插入该区域。

为此,将ItemsControl添加到功能区中的某个窗格中,您希望插入的控件显示在该窗格中。将prism命名空间添加为XAML命名空间,如下所示:

xmlns:prism="http://prismlibrary/"

然后将以下附加属性添加到ItemsControl:

prism:RegionManager.RegionName="CustomModuleCommandRegion"

然后在您的模块中,如果在加载模块时应立即添加命令,则在模块类本身中注入IRegionManager,或者如果在加载特定视图之前它不会发生,则在ViewModel中的其他位置注入IRegionManager。像你描述的一些用户互动:

public ConstructorForModuleOrViewModel(IRegionManager regionManager)
{
    _regionManager = regionManager;
}

private SomeCommandHandler()
{
    var commandButton = // create button and wire up command here)
    _regionManager.AddViewToRegion(commandButton, "CustomModuleCommandRegion");
}

您还可以选择使用区域管理器的RegisterViewWithRegion方法设置工厂方法,或者只指定要注入的视图类型(即按钮)。但是对于一个按钮,您需要在将它放入区域之前(或之后)连接命令处理程序,因此AddViewToRegion可能更合适。如果它是上下文敏感的东西 - 也就是说你只希望按钮显示在功能区中,或许在视图中进行选择时 - 那么你可以先从区域管理器获取该区域,然后使用{{1 Add上的{}}和Remove方法可以动态添加和删除视图(按钮):

IRegion

使用Prism模块和区域的组合,您可以实现应用程序的运行时可扩展性 - 即,这个新功能可能会被放入"无需重新编译应用程序中的主应用程序或其他模块。为此,您需要使用任一配置来指定模块,以便可以在部署环境中编辑以添加模块,或者可以使用DirectoryModuleCatalog在启动时扫描目录中的模块。它甚至可以使用FileSystemWatcher来监视应用程序运行时丢弃的模块的目录,并在放入监视目录时立即点亮它们。