跳到主要内容

8.7 模块注册与初始化

学习目标​

你将把任务功能的页面注册移入 TasksModule,明确模块目录、依赖和初始化的职责。前提:已经完成 启动与容器 以及 Region 导航,使用 Prism.DryIoc 9.0.537。

与后端功能注册的对照​

ASP.NET Core 项目经常用 AddTasksFeature(IServiceCollection) 集中注册某个业务功能。Prism 模块也把一组页面、服务与启动动作放在明确边界中,但还通过模块目录记录何时加载、依赖哪些模块。模块仍运行在同一应用进程,共享容器和资源;它不自动提供进程隔离或 HTTP 请求作用域。

示例把两个页面放进同一个模块,是为了练习注册流程,并非声称每个页面都是独立插件。开始时按业务能力分组比按每个控件拆模块更易维护;共享接口可放到契约程序集,Shell 通过导航名和接口协作,避免引用功能内部实现。

注册类型与初始化​

下面是配套项目的完整模块文件。RegisterTypes 负责页面与 ViewModel 的注册;OnInitialized 接收可用于解析依赖的容器。这里没有需要执行的启动工作,因此初始化方法保持空。

Modules/TasksModule.cs
using Prism.Ioc;
using Prism.Modularity;
using PrismStudy.ViewModels;
using PrismStudy.Views;

namespace PrismStudy.Modules;

public sealed class TasksModule : IModule
{
public void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.RegisterForNavigation<TasksView,
TasksViewModel>("Tasks");
containerRegistry.RegisterForNavigation<SettingsView,
SettingsViewModel>("Settings");
}

public void OnInitialized(IContainerProvider containerProvider)
{
// 本模块只提供注册;首次导航由 Shell.Loaded 触发。
}
}

RegisterForNavigation 同时建立导航名和视图关联。不要又在 App.RegisterTypes 重复注册同名页面;读者应能从模块文件找到功能的组合入口。对话框 Confirmation 仍由 App 注册,属于此示例共享的交互能力。

再将下列方法合并到已有 App.xaml.cs;文件导入 Prism.Modularity 和 PrismStudy.Modules。加入目录的模块默认随启动初始化;类文件存在于项目中并不会让 Prism 自动发现它。

App.xaml.cs:模块目录配置
protected override void ConfigureModuleCatalog(IModuleCatalog moduleCatalog)
{
moduleCatalog.AddModule<TasksModule>();
}

Prism 根据目录中的信息加载模块及其依赖,再调用注册和初始化方法。不要把初始化理解成“全部窗口和 Region 已就绪”。宿主控件可能还没进入可视树,因此配套 Shell 等待 Loaded 后请求 MainRegion 导航;OnInitialized 不提前查找 Region。

依赖与按需加载​

需要基础模块提供的服务时,在模块目录声明依赖,让 Prism 安排初始化顺序,而非假定书写顺序就是执行顺序。循环依赖通常说明职责交错,应抽出双方需要的契约或协调服务。构造函数注入负责对象关系,模块依赖负责加载关系,两者不能互相替代。

按需加载适合不常用且初始化昂贵的功能。下面是独立扩展练习:用第一条语句替换原有目录注册,在用户请求功能时由注入的 IModuleManager 执行第二条语句。不要把同一个模块同时以默认和按需两种模式重复加入目录。

按需加载:位于不同方法的两个片段
// App.ConfigureModuleCatalog 内,替换原来的 AddModule:
moduleCatalog.AddModule<TasksModule>(InitializationMode.OnDemand);

// 功能入口方法内;moduleManager 是注入的 IModuleManager:
moduleManager.LoadModule(nameof(TasksModule));

加载完成、页面注册就绪后再请求导航;不能先导航到未知名称,指望导航服务自动加载模块。真实应用应观察模块加载完成与错误,显示加载状态并防止重复请求;示例的默认启动模式无需增加这层交互。

多程序集与常见错误​

PrismStudy 的模块与 Shell 位于同一程序集。这已经演示模块化组织,但没有实现“把 DLL 丢进目录即可安装”的插件系统。拆成类库时还需要项目或程序集引用、兼容的契约以及明确的目录配置。目录扫描只是发现机制,插件版本、依赖和更新策略仍需要设计。

把大量 HTTP 请求塞进同步 OnInitialized 会拖住启动,也不能通过随手写 async void 获得可观察的初始化结果。把远程加载交给应用服务与页面异步状态;只有在首次使用确实需要数据时才加载。模块也不自动为每个页面建立作用域,服务寿命应在 依赖注入 的原则下明确选择。

不要在模块里新建第二个 DryIoc 或 Microsoft DI 容器,否则同一接口可能对应两份状态,导航、对话框和事件服务难以协同。模块用 Prism 传入的 IContainerRegistry 注册,并通过同一应用容器解析。

练习与验收​

在 RegisterTypes 与 OnInitialized 设置断点,启动后应先看到本模块注册再初始化,首次导航发生在 Shell 加载之后。临时移除 AddModule<TasksModule>():窗口应仍能启动,任务导航应失败,说明 Shell 与功能注册是两个步骤。恢复后,任务和设置都应能打开;全项目对这两个导航名各保留一处注册。

官方资料​

下一节:PrismStudy 综合实践。