WPF入门到跪下 第十一章 Prism(二)启动流程与Shell窗口的创建

📅 发布时间:2026/7/14 12:00:30 👁️ 浏览次数:
WPF入门到跪下 第十一章 Prism(二)启动流程与Shell窗口的创建
1. PrismApplication启动流程全解析当你第一次接触Prism框架时最让人困惑的可能就是那一连串的启动方法调用顺序。我刚开始用Prism时经常在CreateShell和InitializeShell之间犯迷糊——到底哪个先执行数据预加载该写在哪里今天我们就用实际案例把Prism的启动流程掰开揉碎讲清楚。1.1 启动流程的生命周期PrismApplication的启动过程就像一场精心编排的交响乐每个方法都在特定时机被调用。实测下来完整的执行顺序是这样的RegisterTypes这是整个流程的起点负责注册类型到IOC容器。我习惯在这里注册全局服务比如数据库连接、API客户端等单例对象。protected override void RegisterTypes(IContainerRegistry containerRegistry) { // 注册全局单例服务 containerRegistry.RegisterSingletonIApiService, DemoApiService(); // 注册导航视图 containerRegistry.RegisterForNavigationMainView(); }CreateShell创建主窗口实例但此时还不会显示窗口。这里有个坑要注意——必须通过Container.Resolve创建实例否则依赖注入会失效。protected override Window CreateShell() { // 正确做法通过容器解析窗口实例 return Container.ResolveMainWindow(); // 错误示范直接new会导致依赖注入失效 // return new MainWindow(); }InitializeShell主窗口初始化完成的回调点。这里我常做三件事设置Application.Current.MainWindow执行登录验证预加载核心数据protected override void InitializeShell(Window shell) { // 必须设置主窗口否则应用会立即退出 Application.Current.MainWindow shell; // 登录验证示例 var loginDialog Container.ResolveLoginDialog(); if (loginDialog.ShowDialog() ! true) { Application.Current.Shutdown(); return; } // 数据预加载 var dataService Container.ResolveIDataService(); await dataService.PreloadAsync(); shell.Show(); }OnInitialized所有初始化完成后的最终回调。适合做一些收尾工作比如启动后台服务、初始化通知系统等。1.2 关键方法对比通过这个表格可以清晰看出各方法的差异方法调用时机典型用途是否必须实现RegisterTypes最早执行注册IOC类型是CreateShell容器初始化后创建主窗口是InitializeShell主窗口创建后登录验证/数据加载否OnInitialized所有初始化完成后启动后台服务否2. Shell窗口的创建与定制2.1 Shell窗口的最佳实践Shell是Prism应用的外壳我总结了几条实践经验最小化Shell的职责Shell应该只负责布局不包含业务逻辑。就像房子的承重墙只提供结构支撑。使用Region管理内容这是Prism的核心特性。我的典型Shell布局是这样的Window Grid !-- 顶部导航区 -- ContentControl prism:RegionNameHeaderRegion/ !-- 主内容区 -- ContentControl prism:RegionNameMainRegion/ !-- 底部状态栏 -- ContentControl prism:RegionNameFooterRegion/ /Grid /Window处理窗口生命周期重写OnClosed方法清理资源protected override void OnClosed(EventArgs e) { // 释放所有IDisposable资源 Container.Dispose(); base.OnClosed(e); }2.2 动态Shell创建技巧在某些场景下我们可能需要根据条件创建不同的Shell。比如免费版和付费版使用不同的主界面。这时可以这样实现protected override Window CreateShell() { var licenseService Container.ResolveILicenseService(); return licenseService.IsPremium ? Container.ResolvePremiumShell() : Container.ResolveStandardShell(); }3. 登录验证的优雅实现3.1 登录流程设计在商业应用中登录验证是刚需。结合Prism的特点我推荐这种实现模式protected override async void InitializeShell(Window shell) { var authService Container.ResolveIAuthService(); // 自动登录尝试 if (!await authService.TryAutoLoginAsync()) { var loginResult await ShowLoginDialog(); if (!loginResult) Application.Current.Shutdown(); } // 加载用户数据 await LoadUserDataAsync(); base.InitializeShell(shell); shell.Show(); } private Taskbool ShowLoginDialog() { var tcs new TaskCompletionSourcebool(); var loginDialog Container.ResolveLoginDialog(); loginDialog.Closed (_, __) tcs.SetResult(loginDialog.DialogResult true); loginDialog.Show(); return tcs.Task; }3.2 常见问题排查窗口闪退问题忘记设置Application.Current.MainWindow会导致窗口显示后立即关闭依赖注入失效没有通过Container.Resolve创建的对象无法使用DI异步操作阻塞在同步方法中调用异步操作可能导致死锁4. 高级启动配置4.1 模块化加载策略Prism的模块化是其最大特色之一。在启动时可以配置不同的加载策略protected override IModuleCatalog CreateModuleCatalog() { // 方式1代码配置 var catalog new ModuleCatalog(); catalog.AddModuleCoreModule(); // 方式2配置文件加载 // return new ConfigurationModuleCatalog(); // 方式3目录扫描 // return new DirectoryModuleCatalog() { ModulePath .\Modules }; return catalog; } protected override void ConfigureModuleCatalog(IModuleCatalog moduleCatalog) { // 动态配置模块属性 var module moduleCatalog.Modules.First(m m.ModuleName AdminModule); module.InitializationMode InitializationMode.OnDemand; }4.2 异常处理机制完善的错误处理能让应用更健壮。我通常在三个层面添加处理全局异常捕获protected override void OnStartup(StartupEventArgs e) { AppDomain.CurrentDomain.UnhandledException (s, ex) HandleCrash(ex.ExceptionObject as Exception); base.OnStartup(e); }启动过程异常protected override Window CreateShell() { try { return Container.ResolveMainWindow(); } catch (Exception ex) { ShowErrorDialog(启动失败, ex.Message); Application.Current.Shutdown(1); return null; } }异步操作异常protected override async void InitializeShell(Window shell) { try { await CriticalStartupAsync(); } catch (Exception ex) { Logger.Error(ex, 初始化失败); throw; } }在实际项目中我发现合理规划启动流程可以避免很多后期维护的麻烦。建议在项目初期就建立标准的启动模板包括异常处理、日志记录、性能监控等基础功能。当项目规模扩大后这种规范化的优势会更加明显。