为WPF应用程序创建附加组件

问题描述:

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

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

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

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

让我们来想象一下情况。我住在华盛顿,我对这两个按钮感到满意(Add PersonDelete Person)。但住在新泽西的我的朋友鲍勃想在没有重新编译整个应用程序的情况下添加新的按钮Edit Button。也就是说,鲍勃写dll他可以编辑人,然后点击Add new button to applicationRibbonControlModule。之后,EditPerson按钮出现在RibbonControl中,例如,在ContextMenu中。也许EditPerson DLL会是另一个棱镜模块,我不知道。

也就是说,我的要求是:

  • 可插拔控制
  • 是有可能堵塞控制,而不recompliation? (如在浏览器插件或扩展(Classic Notes for Opera)(无需重新启动浏览器来使用插件))
  • 其他程序员可以开发自己的附加组件,而不使用我的源代码
  • 一旦用户插入后一个控件,那么这个新的控件应该总是插在应用程序中。

是否有可能使用WPF,MVVM和Prism?我非常喜欢Prism,不想否认Prism,但如果“最终证明了手段”,我想使用其他技术。

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

+0

另一种选择是插件公开了一个按钮的表示的实现,由您在界面中定义。在编写界面时,您将知道如何将其与用户界面绑定。在ContentControl中抛出实现并使用DataTemplate来设置控件,无论是按钮还是下拉菜单或任何东西。 – Will

+0

@你能提供一些链接看吗? – StepUp

+0

请注意,如果您不想使用它们,通常您不需要MEF或任何其他框架。您只需定义一些加载项开发人员实现的接口(IModule),然后扫描程序集以实现此接口的实现,然后实例化并运行。当然,您必须在整个应用程序中定义扩展点,但无论如何您都必须使用任何框架来完成。 – Evk

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

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

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

这是一个非常涉及的话题,并且给出这个问题的普遍性如果这个问题没有被标记,我会感到惊讶。如果您想了解更多详情,请告诉我们,我们可以将其移至讨论页面。

+0

@StepUp其实我从来没有创造过一个人,只有跟随其他人的链接。尝试[这里的链接](http://chat.*.com/rooms/109889/mef-plugins-for-mvvm),让我知道如果你有任何问题。 –

您可以使用Prism中的Regions进行描述。您可以在功能区中添加一个命名区域,允许Prism模块在模块第一次加载时插入到该区域的新按钮,或者当用户单击您模块的某个用户界面中的按钮时。

要做到这一点,请将ItemsControl添加到功能区中您希望插入的控件显示的某个窗格中。添加棱镜命名空间,像这样一个XAML命名空间:

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可能更合适。如果它是上下文敏感的 - 也就是说,只希望按钮显示在功能区中,或许在视图中进行选择时 - 那么您可以先从区域管理器获取该区域,然后使用AddRemoveIRegion方法来添加和删除您的视图(按钮)动态像这样:

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

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

+0

非常感谢您的回应,Brian。非常感谢你。但在我看来,“MEF”是为描述情况而设计的,并且更适用。这是对的吗?对于可插拔软件来说它是更清晰的架构? – StepUp