Prism for WPF 启动逻辑详解 —— 从 App.xaml.cs 说起
在 WPF 项目中引入 Prism 框架后,第一个映入眼帘的通常是自动生成的 App.xaml.cs,里面只有寥寥几行代码。然而,这几行代码背后隐藏着整个应用程序的启动脉络。理解它们,是掌握 Prism 开发的第一步。
1. 默认代码长什么样?
1 | /// <summary> |
App 类继承自 PrismApplication(而非默认的 Application),这意味着启动流程被 Prism 接管了。我们只需重写两个关键方法,框架就会按部就班地工作。
2. 完整的启动时间线
当程序运行,Main 入口被调用后,Prism 执行以下步骤(这些步骤都在基类中,我们看不到,但必须知晓):
- 创建依赖注入容器(默认使用 Dryloc 或 Unity)。
- 调用
RegisterTypes —— 你可以在这里注册自己的服务(接口与实现类的映射)。 - 调用
CreateShell —— 获取主窗口实例(这里只是new,尚未显示)。 - 自动解析 ViewModel —— Prism 会利用容器构造
Shell 对应的ShellViewModel(如果存在),并自动注入到Shell的构造函数中。 - 显示主窗口 —— 基类调用
shell.Show(),应用程序正式启动。
3. 两个重写方法的深度解读
3.1 CreateShell —— 告诉框架“谁是主窗口”
1 | protected override Window CreateShell() |
- 作用:返回应用程序的主窗口实例。
- 注意:这里只负责 创建视图(View) ,不要在这里编写业务逻辑,也不要手动
newViewModel。ViewModel 会由容器通过构造函数自动注入。 - 常见疑问:为什么没有
Show()?因为显示由基类统一管理,你只需要返回实例。
3.2 RegisterTypes —— 依赖注入的“注册中心”
1 | protected override void RegisterTypes(IContainerRegistry containerRegistry) |
作用:注册所有需要注入的服务。例如,如果你的
ShellViewModel 需要IOrderService,你就必须在这里注册:1
containerRegistry.Register<IOrderService, OrderService>();
支持的生命周期:
Register(瞬时,每次请求新建)RegisterSingleton(单例)RegisterScoped(WPF 中较少用)
空注册为什么能运行? 如果
Shell 和ShellViewModel 都没有任何构造参数,或者你在 XAML 中直接设置了DataContext,那么注册表为空也不会报错。
4. 依赖注入与自动装配 —— Prism 的“黑魔法”
Prism 的 ViewModelLocator 会自动按照命名约定将 View 与 ViewModel 绑定(例如 Shell 对应 ShellViewModel)。这要求:
- ViewModel 必须放在合适的命名空间(通常为
Views 同名或通过ViewModelLocationProvider配置)。 - ViewModel 的构造函数可以接收任何已注册的服务接口,容器会在创建时自动提供实现。
示例:
1 | public class ShellViewModel |
5. 新手最容易踩的三个“坑”
① 程序闪退,没有任何错误弹窗
绝大多数是因为 构造函数中有未注册的服务。Prism 在解析时会抛出 ResolutionFailedException,但由于是启动时异常,往往直接终止进程。
排查方法:在 Visual Studio 中勾选“CLR 异常”的所有 throw,然后调试运行,就能定位到具体哪个类型无法解析。
② 在 CreateShell 里写了 new ShellViewModel(...)
这破坏了依赖注入的理念,导致容器无法管理 ViewModel 的生命周期,也不利于单元测试。正确做法是只 return new Shell();,让容器自动装配。
③ 想延迟显示窗口,却在 CreateShell 里写等待逻辑
应该重写 OnInitialized 方法,在这里执行初始化代码(如加载配置、登录验证),最后调用 base.OnInitialized(),它会自动显示 Shell。
1 | protected override void OnInitialized() |
6. 还可以重写哪些有用的方法?
除了上述两个,PrismApplication 还提供了多个可重写的虚方法,便于扩展:
ConfigureModuleCatalog—— 配置模块加载(用于 Prism 的模块化开发)。ConfigureViewModelLocator—— 自定义 ViewModel 定位规则。RegisterRequiredTypes(内部) —— 已注册 Prism 必需的类型,一般不需要重写。OnStartup —— 如果你需要更早介入,可以重写它,但务必调用base.OnStartup(e)。
7. 总结一句话
RegisterTypes 是“准备食材”(注册服务),CreateShell 是“拿出主锅”(创建主窗口),Prism 框架负责“开火做饭”(构建依赖并显示窗口)。
掌握这个启动逻辑,你就能从容地构建 Prism 应用,并享受依赖注入带来的松耦合和可测试性。后续深入学习模块化、导航、事件聚合等高级特性时,这个基础认知会一直陪伴你。