为WPF应用程序创建插件

5
我有一个简单的数据库应用程序,用户可以添加或删除人员。此外,该应用程序还有一个名为“将新按钮添加到应用程序”的按钮。该应用程序使用Prism框架构建。有两个模块:
  • RibbonControlModule(包含三个按钮 - 添加人员删除人员将新按钮添加到应用程序

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

我的要求是在运行时添加新的按钮。
让我们想象一种情况。我住在华盛顿,很喜欢这两个按钮(Add PersonDelete Person)。但是我的朋友Bob住在新泽西州,他想添加一个新的按钮Edit Button,而不需要重新编译整个应用程序。也就是说,Bob编写了dll文件,在其中可以编辑人员信息,然后在中单击Add new button to application。之后,EditPerson按钮会出现在中,并可能出现在中。也许EditPerson dll将是另一个Prism模块,我不知道。
我的要求如下:
  • 可插拔的控件
  • 是否可以在不重新编译的情况下插入控件?(类似于浏览器中的附加组件或扩展(Opera的Classic Notes),无需重启浏览器即可使用附加组件)
  • 其他程序员可以开发自己的附加组件而不使用我的源代码
  • 用户插入控件后,这个新控件应始终保持插入状态。

使用WPF、MVVM和Prism是否可能?我非常喜欢Prism,不想放弃它,但如果“目的正当”,我愿意使用其他技术。

如果可能,那么我该如何做呢?


另一种选择是插件公开你在接口中定义的按钮表示实现。由于你编写了接口,因此你将知道如何将其绑定到UI上。将实现放入ContentControl中,并使用DataTemplate设置控件,无论是按钮、下拉框还是其他任何东西。 - user1228
@Will,你能提供一些链接让我查看吗? - StepUp
请注意,通常情况下,如果您不想使用MEF或任何其他框架,您都不需要它们。您只需定义一些接口(IModule),插件开发人员实现这些接口,然后扫描实现此接口的程序集,再进行实例化和运行即可。当然,您必须在整个应用程序中定义扩展点,但无论如何,您都必须使用任何框架来完成这项工作。 - Evk
@Evk,你能提供一些链接让我查看吗? - StepUp
1
很遗憾没有链接,只有我的个人经验。我最初也使用了MEF,在大多数情况下它都很好,我想你也应该尝试一下。你只是问“是否可以使用WPF、MVVM和Prism”,我的观点是,是的,这肯定是可能的。 - Evk
2个回答

4

这就是MEF插件架构的设计目的。

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

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

这是一个非常复杂的话题,鉴于这个问题的普遍性,如果没有被标记,我会感到惊讶。如果您需要更多详细信息,请告诉我,我们可以将其移动到讨论页面。


@StepUp 实际上我从未自己创建过,只是一直关注其他人的链接。尝试使用这个链接,如果有任何问题,请告诉我。 - Mark Feldman

4
您可以通过Prism中的Regions来实现您所描述的功能。在Ribbon中添加一个命名区域,使得Prism模块在加载时或后续用户单击模块UI中的按钮时插入新的按钮到该区域中。
为此,在您希望插入控件显示的Ribbon的某个面板中添加一个ItemsControl。像这样在XAML名称空间中添加Prism的名称空间: xmlns:prism="http://prismlibrary/" 然后将以下附加属性添加到您的ItemsControl中: prism:RegionManager.RegionName="CustomModuleCommandRegion" 接下来,在您的模块中,将IRegionManager注入到模块类本身中(如果应该在加载模块时添加命令),或在ViewModel中的其他位置(如果不会在特定视图加载或像您描述的某些用户交互之前发生)。
public ConstructorForModuleOrViewModel(IRegionManager regionManager)
{
    _regionManager = regionManager;
}

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

您也可以使用区域管理器的RegisterViewWithRegion方法来设置一个工厂方法,或仅指定您想要注入的视图类型(即按钮)。但是对于按钮,您需要在将其放入区域之前(或之后)连接命令处理程序,因此AddViewToRegion可能更合适。如果它是一些上下文相关的东西 - 即您只希望在选择视图中进行选择时,在功能区中显示按钮 - 那么您可以先从区域管理器获取区域,然后像以下方式动态添加和删除您的视图(按钮):IRegion上的AddRemove方法。
IRegion region = _regionManager.Regions["CustomModuleCommandRegion"];
region.Add(myCommandView);
...
region.Remove(myCommandView);

使用Prism模块和区域的组合,您可以实现应用程序的运行时可扩展性 - 也就是说,这个新功能可以"插入",而不需要重新编译主应用程序或应用程序中的其他模块。为此,您需要使用配置来指定模块,以便在部署环境中编辑添加模块,或者您可以使用DirectoryModuleCatalog在启动时扫描目录以查找模块。甚至可以使用FileSystemWatcher监视目录以查看在应用程序运行时放置的模块,并在放置在受监视的目录中时立即使它们生效。

非常感谢您的回复,Brian。非常感谢。但是在我看来,MEF 是为描述的情况设计的,更加适用。这是正确的吗?并且它是可插入软件更清晰的架构? - StepUp

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接