为 WPF 应用程序创建加载项
Creating add-ons for WPF applications
我有一个简单的数据库应用程序,用户可以在其中添加或删除人员。此外,应用程序有一个按钮"Add new button to application"。此应用程序是使用 Prism
框架构建的。有两个模块:
RibbonControlModule(包含三个按钮 - Add Person
、Delete Person
、Add new button to application
)
PersonModule(包含人员增删逻辑)
我的要求是在运行时添加新按钮。
让我们想象一个情况。我住在华盛顿,我对这两个按钮(Add Person
和 Delete 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,但是如果"the end justifies the means",我想用其他技术。
如果可以,我该怎么做?
这就是 MEF plugin architecture 的设计目的。
简而言之,您创建一个包含插件接口的 SDK,并将其作为独立库提供给您的客户。然后,您客户端的插件会实现此接口并使用 MEF Export
属性导出它们,然后您的主应用程序会导入该属性。
有点棘手的是数据模板,这通常是 MVVM 的关键组件。长话短说,您的插件需要将它们的数据模板放在资源字典中,为该字典提供它自己的部分 class 文件,并使用 MEF 的 [Export]
属性导出它。然后您的主应用程序需要导入这些并将它们添加到全局 ResourceDictionary 的 'MergedDictionaries' 数组中。这通常与在单独的通道中导入的所有视图模型 classes 分开完成。最终效果是您的插件可以在运行时同时提供视图和视图模型,以及将两者绑定在一起的数据模板,并且它们都将像静态编译到您的原始应用程序中一样工作。这也意味着您可以为您的客户创建插件 API,而无需暴露主应用程序的内部结构。
这是一个非常复杂的话题,考虑到这个问题的普遍性,如果这个问题没有被标记,我会感到惊讶。如果您想了解更多详细信息,请告诉我,我们可以将其移至讨论页面。
您可以使用 Prism 中的区域来执行您所描述的操作。您可以将一个命名区域添加到您的功能区,允许 Prism 模块在模块首次加载时或稍后当用户单击您描述的某些 UI 模块中的按钮时将新按钮插入该区域。
为此,将 ItemsControl 添加到功能区内的某个窗格中,您希望插入的控件显示在该窗格中。将 prism 命名空间添加为 XAML 命名空间,如下所示:
xmlns:prism="http://prismlibrary/"
然后将以下附件 属性 添加到您的 ItemsControl 中:
prism:RegionManager.RegionName="CustomModuleCommandRegion"
然后在您的模块中,如果在加载模块后应立即添加命令,则在模块 class 本身中注入 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
可能更合适。如果它是上下文相关的东西——即你只希望按钮显示在功能区中,也许是在视图中进行选择时——那么你可以先从区域管理器获取区域,然后使用 Add
和 Remove
方法在 IRegion
上动态添加和删除您的视图(按钮),如下所示:
IRegion region = _regionManager.Regions["CustomModuleCommandRegion"];
region.Add(myCommandView);
...
region.Remove(myCommandView);
结合使用 Prism 模块和区域,您可以实现应用程序的运行时可扩展性 - 即此新功能可以 "dropped in" 而无需重新编译主应用程序或应用程序中的其他模块。为此,您需要使用任一配置来指定模块,以便可以在部署的环境中进行编辑以添加模块,或者您可以使用 DirectoryModuleCatalog 在启动时扫描目录以查找模块。甚至可以使用 FileSystemWatcher 来监视在应用程序 运行 时放入的模块的目录,并在放置在监视目录中时让它们立即亮起。
我有一个简单的数据库应用程序,用户可以在其中添加或删除人员。此外,应用程序有一个按钮"Add new button to application"。此应用程序是使用 Prism
框架构建的。有两个模块:
RibbonControlModule(包含三个按钮 -
Add Person
、Delete Person
、Add new button to application
)PersonModule(包含人员增删逻辑)
我的要求是在运行时添加新按钮。
让我们想象一个情况。我住在华盛顿,我对这两个按钮(Add Person
和 Delete 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,但是如果"the end justifies the means",我想用其他技术。
如果可以,我该怎么做?
这就是 MEF plugin architecture 的设计目的。
简而言之,您创建一个包含插件接口的 SDK,并将其作为独立库提供给您的客户。然后,您客户端的插件会实现此接口并使用 MEF Export
属性导出它们,然后您的主应用程序会导入该属性。
有点棘手的是数据模板,这通常是 MVVM 的关键组件。长话短说,您的插件需要将它们的数据模板放在资源字典中,为该字典提供它自己的部分 class 文件,并使用 MEF 的 [Export]
属性导出它。然后您的主应用程序需要导入这些并将它们添加到全局 ResourceDictionary 的 'MergedDictionaries' 数组中。这通常与在单独的通道中导入的所有视图模型 classes 分开完成。最终效果是您的插件可以在运行时同时提供视图和视图模型,以及将两者绑定在一起的数据模板,并且它们都将像静态编译到您的原始应用程序中一样工作。这也意味着您可以为您的客户创建插件 API,而无需暴露主应用程序的内部结构。
这是一个非常复杂的话题,考虑到这个问题的普遍性,如果这个问题没有被标记,我会感到惊讶。如果您想了解更多详细信息,请告诉我,我们可以将其移至讨论页面。
您可以使用 Prism 中的区域来执行您所描述的操作。您可以将一个命名区域添加到您的功能区,允许 Prism 模块在模块首次加载时或稍后当用户单击您描述的某些 UI 模块中的按钮时将新按钮插入该区域。
为此,将 ItemsControl 添加到功能区内的某个窗格中,您希望插入的控件显示在该窗格中。将 prism 命名空间添加为 XAML 命名空间,如下所示:
xmlns:prism="http://prismlibrary/"
然后将以下附件 属性 添加到您的 ItemsControl 中:
prism:RegionManager.RegionName="CustomModuleCommandRegion"
然后在您的模块中,如果在加载模块后应立即添加命令,则在模块 class 本身中注入 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
可能更合适。如果它是上下文相关的东西——即你只希望按钮显示在功能区中,也许是在视图中进行选择时——那么你可以先从区域管理器获取区域,然后使用 Add
和 Remove
方法在 IRegion
上动态添加和删除您的视图(按钮),如下所示:
IRegion region = _regionManager.Regions["CustomModuleCommandRegion"];
region.Add(myCommandView);
...
region.Remove(myCommandView);
结合使用 Prism 模块和区域,您可以实现应用程序的运行时可扩展性 - 即此新功能可以 "dropped in" 而无需重新编译主应用程序或应用程序中的其他模块。为此,您需要使用任一配置来指定模块,以便可以在部署的环境中进行编辑以添加模块,或者您可以使用 DirectoryModuleCatalog 在启动时扫描目录以查找模块。甚至可以使用 FileSystemWatcher 来监视在应用程序 运行 时放入的模块的目录,并在放置在监视目录中时让它们立即亮起。