C# WPF Prism 实战:用模块化 + 导航 + 事件聚合 + 对话框搭一个完整应用

📅 2026/8/21 8:20:35
C# WPF Prism 实战:用模块化 + 导航 + 事件聚合 + 对话框搭一个完整应用
C# WPF Prism 实战用模块化 导航 事件聚合 对话框搭一个完整应用前言前面四篇分别讲了入门、Region/模块化、导航、事件聚合与对话框。本文把它们串起来搭一个麻雀虽小、五脏俱全的多模块桌面应用——一个简化的用户管理客户端。功能登录 → 用户列表可查看详情、删除→ 设置页登录状态通过事件聚合通知到状态栏删除前用对话框确认。版本说明基于Prism 9.xPrism.DryIocPrism.Events。Region 在Prism.Navigation.Regions对话框在Prism.Dialogs事件在Prism.Events模块在Prism.Modularity。一、整体架构UserManager/ ├─ App.xaml / App.xaml.cs (Bootstrapper 模块目录) ├─ Views/ │ ├─ MainWindow.xaml (Shell菜单区 内容区 状态栏) │ └─ ... ├─ Modules/ │ ├─ Login/ (登录模块) │ │ ├─ LoginModule.cs │ │ └─ Views/LoginView.xaml ViewModels/LoginViewModel.cs │ ├─ Users/ (用户模块) │ │ ├─ UsersModule.cs │ │ └─ Views/UserListView.xaml UserDetailView.xaml ViewModels/... │ └─ Settings/ (设置模块) │ ├─ SettingsModule.cs │ └─ Views/SettingsView.xaml ViewModels/SettingsViewModel.cs ├─ Events/UserLoggedInEvent.cs (跨模块事件) └─ Dialogs/ConfirmDialog.xaml ViewModels/ConfirmDialogViewModel.cs二、Shell三块区域!-- MainWindow.xaml --Windowx:ClassUserManager.Views.MainWindowxmlnshttp://schemas.microsoft.com/winfx/2006/xaml/presentationxmlns:xhttp://schemas.microsoft.com/winfx/2006/xamlxmlns:prismhttp://prismlibrary.com/Title用户管理Height500Width800DockPanel!-- 顶部菜单 --ListBoxDockPanel.DockTopprism:RegionManager.RegionNameMenuRegion/!-- 底部状态栏 --StatusBarDockPanel.DockBottomStatusBarItemTextBlockText{Binding StatusText}//StatusBarItem/StatusBar!-- 中间内容 --ContentControlprism:RegionManager.RegionNameContentRegion//DockPanel/Window这里把状态栏TextBlock直接绑到 Shell 的 ViewModelMainWindowViewModel它订阅登录事件更新状态避免为状态栏单独建一个区域。三、BootstrapperusingSystem.Windows;usingPrism.DryIoc;usingPrism.Ioc;usingPrism.Modularity;usingPrism.Navigation.Regions;usingUserManager.Dialogs;usingUserManager.Modules.Login;usingUserManager.Modules.Users;usingUserManager.Modules.Settings;usingUserManager.Views;namespaceUserManager{publicpartialclassApp:PrismApplication{protectedoverrideWindowCreateShell(){returnContainer.ResolveMainWindow();}protectedoverridevoidRegisterTypes(IContainerRegistrycontainerRegistry){containerRegistry.RegisterForNavigationLoginView();containerRegistry.RegisterForNavigationUserListView();containerRegistry.RegisterForNavigationUserDetailView();containerRegistry.RegisterForNavigationSettingsView();// 注册对话框containerRegistry.RegisterDialogConfirmDialog,ConfirmDialogViewModel();}protectedoverridevoidConfigureModuleCatalog(IModuleCatalogmoduleCatalog){moduleCatalog.AddModuleLoginModule();moduleCatalog.AddModuleUsersModule();moduleCatalog.AddModuleSettingsModule();}protectedoverridevoidOnInitialized(){base.OnInitialized();Container.ResolveIRegionManager().RequestNavigate(ContentRegion,nameof(LoginView));}}}四、跨模块事件// Events/UserLoggedInEvent.csusingPrism.Events;namespaceUserManager.Events{publicclassUserLoggedInEvent:PubSubEventstring{}}五、登录模块// Modules/Login/LoginModule.csusingPrism.Ioc;usingPrism.Modularity;usingPrism.Navigation.Regions;usingUserManager.Modules.Login.Views;namespaceUserManager.Modules.Login{publicclassLoginModule:IModule{privatereadonlyIRegionManager_regionManager;publicLoginModule(IRegionManagerregionManager)_regionManagerregionManager;publicvoidOnInitialized(IContainerProvidercontainerProvider){_regionManager.RegisterViewWithRegionLoginMenuView(MenuRegion);}publicvoidRegisterTypes(IContainerRegistrycontainerRegistry){}}}// Modules/Login/ViewModels/LoginViewModel.csusingPrism.Commands;usingPrism.Events;usingPrism.Mvvm;usingPrism.Navigation.Regions;usingUserManager.Events;usingUserManager.Modules.Users.Views;namespaceUserManager.Modules.Login.ViewModels{publicclassLoginViewModel:BindableBase{privatereadonlyIRegionManager_regionManager;privatereadonlyIEventAggregator_eventAggregator;privatestring_usernamestring.Empty;publicstringUsername{get_username;setSetProperty(ref_username,value);}publicDelegateCommandLoginCommand{get;}publicLoginViewModel(IRegionManagerregionManager,IEventAggregatoreventAggregator){_regionManagerregionManager;_eventAggregatoreventAggregator;LoginCommandnewDelegateCommand(OnLogin);}privatevoidOnLogin(){// 发布登录事件通知状态栏等其他模块_eventAggregator.GetEventUserLoggedInEvent().Publish(Username);// 导航到用户列表_regionManager.RequestNavigate(ContentRegion,nameof(UserListView));}}}六、状态栏Shell ViewModel订阅事件// ViewModels/MainWindowViewModel.csusingPrism.Events;usingPrism.Mvvm;usingUserManager.Events;namespaceUserManager.ViewModels{publicclassMainWindowViewModel:BindableBase{privatestring_statusText未登录;publicstringStatusText{get_statusText;setSetProperty(ref_statusText,value);}publicMainWindowViewModel(IEventAggregatoreventAggregator){eventAggregator.GetEventUserLoggedInEvent().Subscribe(OnUserLoggedIn,ThreadOption.UIThread);}privatevoidOnUserLoggedIn(stringusername){StatusText$当前用户{username};}}}注意MainWindowViewModel通过约定MainWindow→MainWindowViewModel自动绑定无需额外配置。七、用户模块列表 详情 删除确认// Modules/Users/ViewModels/UserListViewModel.csusingSystem.Collections.ObjectModel;usingPrism.Commands;usingPrism.Dialogs;usingPrism.Mvvm;usingPrism.Navigation.Regions;usingUserManager.Modules.Users.Views;namespaceUserManager.Modules.Users.ViewModels{publicclassUserListViewModel:BindableBase{privatereadonlyIRegionManager_regionManager;privatereadonlyIDialogService_dialogService;publicObservableCollectionstringUsers{get;}new();publicDelegateCommandstringDetailCommand{get;}publicDelegateCommandstringDeleteCommand{get;}publicUserListViewModel(IRegionManagerregionManager,IDialogServicedialogService){_regionManagerregionManager;_dialogServicedialogService;DetailCommandnewDelegateCommandstring(OnDetail);DeleteCommandnewDelegateCommandstring(OnDelete);Users.Add(张三);Users.Add(李四);Users.Add(王五);}privatevoidOnDetail(stringname){varpnewNavigationParameters{{name,name}};_regionManager.RequestNavigate(ContentRegion,nameof(UserDetailView),p);}privatevoidOnDelete(stringname){varpnewDialogParameters{{message,$确定删除用户{name}吗}};_dialogService.ShowDialog(ConfirmDialog,p,result{if(result.ResultButtonResult.OK){Users.Remove(name);}});}}}DelegateCommandstring的命令参数在 XAML 里用CommandParameter{Binding}把当前列表项传进去ListBoxItemsSource{Binding Users}ListBox.ItemTemplateDataTemplateStackPanelOrientationHorizontalTextBlockText{Binding}Width120/ButtonContent详情Command{Binding DataContext.DetailCommand, RelativeSource{RelativeSource AncestorTypeListBox}}CommandParameter{Binding}Margin4/ButtonContent删除Command{Binding DataContext.DeleteCommand, RelativeSource{RelativeSource AncestorTypeListBox}}CommandParameter{Binding}Margin4//StackPanel/DataTemplate/ListBox.ItemTemplate/ListBox八、对话框// Dialogs/ViewModels/ConfirmDialogViewModel.csusingSystem;usingPrism.Commands;usingPrism.Dialogs;usingPrism.Mvvm;namespaceUserManager.Dialogs{publicclassConfirmDialogViewModel:BindableBase,IDialogAware{publicstringTitle确认;publiceventActionIDialogResult?RequestClose;privatestring_messagestring.Empty;publicstringMessage{get_message;setSetProperty(ref_message,value);}publicDelegateCommandOkCommand{get;}publicDelegateCommandCancelCommand{get;}publicConfirmDialogViewModel(){OkCommandnewDelegateCommand(()RequestClose?.Invoke(newDialogResult(ButtonResult.OK)));CancelCommandnewDelegateCommand(()RequestClose?.Invoke(newDialogResult(ButtonResult.Cancel)));}publicboolCanCloseDialog()true;publicvoidOnDialogClosed(){}publicvoidOnDialogOpened(IDialogParametersparameters){Messageparameters.GetValuestring(message);}}}!-- Dialogs/ConfirmDialog.xaml --UserControlx:ClassUserManager.Dialogs.ConfirmDialogxmlnshttp://schemas.microsoft.com/winfx/2006/xaml/presentationxmlns:xhttp://schemas.microsoft.com/winfx/2006/xamlxmlns:prismhttp://prismlibrary.com/prism:ViewModelLocator.AutoWireViewModelTrueWidth320StackPanelMargin20TextBlockText{Binding Message}TextWrappingWrapMargin0,0,0,20/StackPanelOrientationHorizontalHorizontalAlignmentRightButtonContent确定Command{Binding OkCommand}Width80Margin0,0,10,0/ButtonContent取消Command{Binding CancelCommand}Width80//StackPanel/StackPanel/UserControl九、设置模块展示模块独立性// Modules/Settings/SettingsModule.csusingPrism.Ioc;usingPrism.Modularity;usingPrism.Navigation.Regions;usingUserManager.Modules.Settings.Views;namespaceUserManager.Modules.Settings{publicclassSettingsModule:IModule{privatereadonlyIRegionManager_regionManager;publicSettingsModule(IRegionManagerregionManager)_regionManagerregionManager;publicvoidOnInitialized(IContainerProvidercontainerProvider){_regionManager.RegisterViewWithRegionSettingsMenuView(MenuRegion);}publicvoidRegisterTypes(IContainerRegistrycontainerRegistry){}}}设置页本身只是一个普通的 View ViewModel注册为可导航视图后即可用RequestNavigate跳转。十、总结架构Shell 只定义区域具体功能拆成模块模块各自往区域注册视图。通信跨模块用IEventAggregator发布/订阅模块间不直接引用。导航内容切换用RequestNavigate列表/详情跳转带NavigationParameters传参。弹窗删除确认用IDialogServiceIDialogAware纯 MVVM无new Window。命名空间Prism 9RegionPrism.Navigation.Regions导航参数Prism.Navigation对话框Prism.Dialogs事件Prism.Events模块Prism.ModularityDI/注册Prism.Ioc。至此Prism 的核心能力你已经全部掌握可以动手把项目改造成模块化架构了。这套系列入门 → Region/模块化 → 导航 → 事件/对话框 → 实战到这里完结。