【问题标题】:Creating add-ons for WPF applications为 WPF 应用程序创建加载项
【发布时间】:2016-08-15 19:58:18
【问题描述】:

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

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

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

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

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

也就是说,我的要求是:

  • 可插拔控件
  • 是否可以在不重新编译的情况下插入控件? (如浏览器中的插件或扩展(Classic Notes for Opera)(无需重新启动浏览器即可使用插件))
  • 其他程序员可以在不使用我的源代码的情况下开发他们的插件
  • 一旦用户插入一个控件,那么这个新控件应该始终插入到应用程序中。

是否可以使用 WPF、MVVM 和 Prism?我真的很喜欢 Prism,也不想否定 Prism,但如果“目的是正当的”,我想使用其他技术。

如果可以,那我该怎么做呢?

【问题讨论】:

  • 另一种选择是插件公开按钮表示的实现,由您在界面中定义。在编写界面时,您将知道如何将其与 UI 绑定。将实现放入 ContentControl 并使用 DataTemplate 设置控件,无论是按钮还是下拉菜单或任何东西。
  • @你能提供一些链接看看吗?
  • 请注意,如果您不想使用它们,通常不需要 MEF 或任何其他框架。您只需定义插件开发人员实现的一些接口(IModule),然后扫描程序集以查找此接口的实现,然后实例化并运行。当然,您必须在整个应用程序中定义扩展点,但无论如何您都必须对任何框架执行此操作。
  • @Evk 你能提供一些链接看看吗?
  • 不幸的是没有链接,只是自己的经验。我也第一次使用 MEF,对于大多数事情来说它都很好,我想你也应该尝试一下。您刚刚问过“是否可以使用 WPF、MVVM 和 Prism”,我的意思是,是的,这当然是可能的。

标签: c# wpf mvvm prism


【解决方案1】:

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

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

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

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

【讨论】:

  • @StepUp 实际上我从来没有自己创建过,只是关注过其他人的链接。试试this link here,如果您有任何问题,请告诉我。
【解决方案2】:

您可以使用 Prism 中的区域来执行您所描述的操作。您可以向功能区添加一个命名区域,允许 Prism 模块在模块首次加载时或稍后当用户单击您所描述的模块的某些 UI 中的按钮时将新按钮插入该区域。

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

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

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

prism:RegionManager.RegionName="CustomModuleCommandRegion"

然后在您的模块中,如果在加载模块后应立即添加命令,则在 Module 类本身中注入 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 可能更合适。如果它是上下文敏感的——即你只希望按钮显示在功能区中,也许当在视图中进行选择时——那么你可以先从区域管理器获取区域,然后使用AddIRegion 上的Remove 方法来动态添加和删除您的视图(按钮),如下所示:

IRegion region = _regionManager.Regions["CustomModuleCommandRegion"];
region.Add(myCommandView);
...
region.Remove(myCommandView);

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

【讨论】:

  • 非常感谢您的回复,布赖恩。非常感谢。但在我看来,MEF 是为描述的情况而设计的,它更适用。这是正确的吗?更清晰的可插拔软件架构?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-08-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多