Prism_06_Modules
通常,为了构建一个低耦合、高内聚的应用程序,我们一般选择分层。WPF通过MVVM模式将程序分为 View-ViewModel-Model ,很大程度上消除了业务逻辑和界面元素之间的高耦合,使得开发人员将更多精力放在业务逻辑层面,UI界面可以交给更专业的UI人员。
通常,一个应用程序由不同的业务模块来组合而成,理想情况下,每个业务模块有独立的功能;业务模块之间是低耦合关系的;每个业务模块能够单独来开发测试和部署。这样程序是非常容易扩展、测试和维护,而Prism提供了将程序模块化的功能。
下图是Prism中具有多个模块的程序设计:
IModule
模块是功能和资源的逻辑集合,可以单独开发、测试、部署和集成到应用程序中。每个模块都有一个中心类,负责初始化模块并将其功能集成到应用程序中,该类实现了 IModule 接口。
首先,当模块被加载时,RegisterTypes 首先被调用来注册模块实现的任何服务和功能。
然后,调用 OnInitialized 方法,执行View 的注册或其他初始化代码。
public class MyModule : IModule
{
public void RegisterTypes(IContainerRegistry containerRegistry)
{
// register with the container that SomeService implements ISomeService
// ISomeService is defined in the Infrastructure module, see app architecture diagram
containerRegistry.Register<MyApplication.Infrastructure.ISomeService, SomeService>();
}
public void OnInitialized(IContainerProvider containerProvider)
{
// use the containerProvider to retrieve the instance of the Prism RegionManager
// and register the view in this module with a specific region in the app
var regionManager = containerProvider.Resolve<IRegionManager>();
regionManager.RegisterViewWithRegion("MyModuleView", typeof(Views.ThisModuleView));
}
}
ModuleCatalog
ModuleCatalog 保存着程序可以使用的模块信息,本质上是 ModuleInfo 的集合。创建 ModuleInfo 的方法有以下几种:
- 在代码中注册
- 在XAML中注册
- 在配置文件中注册
- 在磁盘中发现
在代码中注册
protected override void ConfigureModuleCatalog()
{
Type moduleCType = typeof(ModuleC);
ModuleCatalog.AddModule(new ModuleInfo()
{
ModuleName = moduleCType.Name,
ModuleType = moduleCType.AssemblyQualifiedName,
InitializationMode = InitializationMode.OnDemand, // 按需加载
});
}
还需要使用 Prism 提供的声明属性:
[Module(ModuleName = "ModuleA")]
[ModuleDependency("ModuleD")]
public class ModuleA : IModule
{
}
在XAML中注册
通常 .xaml 文件作为资源添加到 shell 项目中。ModuleCatalog 是由 App 通过调用 CreateFromXaml 方法创建。
<--! ModulesCatalog.xaml -->
<Modularity:ModuleCatalog xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:Modularity="clr-namespace:Microsoft.Practices.Prism.Modularity;assembly=Microsoft.Practices.Prism">
<Modularity:ModuleInfoGroup Ref="file://DirectoryModules/ModularityWithMef.Desktop.ModuleB.dll" InitializationMode="WhenAvailable">
<Modularity:ModuleInfo ModuleName="ModuleB" ModuleType="ModularityWithMef.Desktop.ModuleB, ModularityWithMef.Desktop.ModuleB, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
</Modularity:ModuleInfoGroup>
<Modularity:ModuleInfoGroup InitializationMode="OnDemand">
<Modularity:ModuleInfo Ref="file://ModularityWithMef.Desktop.ModuleE.dll" ModuleName="ModuleE" ModuleType="ModularityWithMef.Desktop.ModuleE, ModularityWithMef.Desktop.ModuleE, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
<Modularity:ModuleInfo Ref="file://ModularityWithMef.Desktop.ModuleF.dll" ModuleName="ModuleF" ModuleType="ModularityWithMef.Desktop.ModuleF, ModularityWithMef.Desktop.ModuleF, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null">
<Modularity:ModuleInfo.DependsOn>
<sys:String>ModuleE</sys:String>
</Modularity:ModuleInfo.DependsOn>
</Modularity:ModuleInfo>
</Modularity:ModuleInfoGroup>
<!-- Module info without a group -->
<Modularity:ModuleInfo Ref="file://DirectoryModules/ModularityWithMef.Desktop.ModuleD.dll" ModuleName="ModuleD" ModuleType="ModularityWithMef.Desktop.ModuleD, ModularityWithMef.Desktop.ModuleD, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
</Modularity:ModuleCatalog>
ModuleInfoGroups 方便对同一程序集中的模块进行分组。在 ModuleInfoGroups 中可以定义模块间依赖。
protected override IModuleCatalog CreateModuleCatalog()
{
return ModuleCatalog.CreateFromXaml(new Uri("/MyProject;component/ModulesCatalog.xaml", UriKind.Relative));
}
配置文件
可以在 App.config 文件中指定模块信息,此时在运行时添加或删除模块变得容易,而无需再重新编译程序。
<!-- ModularityWithUnity.Desktop\\app.config -->
<xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name="modules" type="Prism.Modularity.ModulesConfigurationSection, Prism.Wpf"/>
</configSections>
<modules>
<module assemblyFile="ModularityWithUnity.Desktop.ModuleE.dll" moduleType="ModularityWithUnity.Desktop.ModuleE, ModularityWithUnity.Desktop.ModuleE, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" moduleName="ModuleE" startupLoaded="false" />
<module assemblyFile="ModularityWithUnity.Desktop.ModuleF.dll" moduleType="ModularityWithUnity.Desktop.ModuleF, ModularityWithUnity.Desktop.ModuleF, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" moduleName="ModuleF" startupLoaded="false">
<dependencies>
<dependency moduleName="ModuleE"/>
</dependencies>
</module>
</modules>
</configuration>
即使程序集在全局程序集缓存中或与程序位于同一文件夹中,assemblyFile 属性也是必须的。
还需要覆盖 CreateModuleCatalog 方法:
protected override IModuleCatalog CreateModuleCatalog()
{
return new ConfigurationModuleCatalog();
}
在目录中发现
Prism 允许将本地目录指定为 WPF 中的模块目录,然后自动扫描指定的文件夹并搜索程序集。要使用这种方法,需要在模块类上使用声明属性来指定模块名称和依赖。
protected override IModuleCatalog CreateModuleCatalog()
{
return new DirectoryModuleCatalog() {ModulePath = @".\\Modules"};
}
[Module(ModuleName = "ModuleA", OnDemand = true)]
[ModuleDependency("ModuleD")]
public class ModuleA : IModule
{
}
模块间通信
下面是几种松耦合的通信模式,都有各自的优点,通常会结合使用。
- 事件 - 模块可以订阅其他模块发布的事件来实现通信。但可能会难以维护,特别是要将许多事件编排在一起完成某个任务时。这时考虑共享服务。
- 共享服务 - 通过公共接口访问的类。通常位于共享程序集并提供系统范围的服务,例如身份验证、日志记录或配置
- 共享资源 - 比如数据库或一组 Web 服务
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义