简介
WPF 项目刚开始时,窗口、按钮和业务代码放在一起,开发速度很快:
csharp
private void SaveButton_Click(object sender, RoutedEventArgs e)
{
// 读取输入框
// 校验数据
// 保存数据库
// 弹出提示
}
页面少时问题不明显。功能一多,代码后面通常会变成:
- 一个窗口里塞着几十个按钮事件;
- View 直接创建服务和仓储;
- 页面之间相互引用,改一个模块牵动多个窗口;
- 业务逻辑只能通过启动 WPF 窗口测试;
- 菜单、内容区、弹窗和模块加载没有统一规则。
Prism 是面向 XAML 应用的框架,主要解决 MVVM、依赖注入、模块化、区域组合、导航、命令和跨模块通信这些问题。它最常见于 WPF,也提供 .NET MAUI、Avalonia、Uno 等平台的对应实现。
一句话理解:
Prism 把一个容易越写越大的 XAML 客户端,拆成 View、ViewModel、服务和模块,并提供一套让这些部分协作的基础设施。
Prism 不是控件库,也不是数据库框架。它不会替应用决定业务边界,重点是把 UI 组合、对象创建和模块通信这些重复工作整理起来。
Prism 解决的核心问题
传统 WPF 常见结构:
text
MainWindow.xaml
MainWindow.xaml.cs
├── 查询数据
├── 保存数据
├── 创建服务
├── 打开子窗口
└── 更新其他控件
Prism 更推荐:
text
View
↓ Binding / Command
ViewModel
↓ 构造函数注入
Application Service
↓
Repository / API Client
大型客户端还可以继续拆成模块:
text
Shell
├── DashboardModule
├── OrderModule
├── ReportModule
└── SettingsModule
每个模块可以拥有自己的 View、ViewModel、服务和注册代码。Shell 只提供整体布局和区域,不需要了解每个模块内部的实现。
Prism 的主要组成
| 组件 | 作用 |
|---|---|
| PrismApplication | 替代普通 WPF Application,负责容器、Shell 和模块启动 |
| IContainerRegistry | 注册服务、View 和导航类型 |
| BindableBase | 提供属性变更通知和 SetProperty |
| DelegateCommand | 把按钮操作转换为可绑定命令 |
| Region | Shell 中可以动态放入 View 的命名区域 |
| IRegionManager | 管理区域和区域导航 |
| IModule | 描述一个可独立加载的业务模块 |
| IEventAggregator | 通过发布订阅进行松耦合通信 |
| INavigationAware | 参与导航前后、接收导航参数 |
| IDialogService | 以 MVVM 方式显示对话框 |
Prism 9 的容器包与平台包分开。WPF 常用:
shell
dotnet add package Prism.DryIoc
也可以选择:
shell
dotnet add package Prism.Unity
本文 Demo 使用 DryIoc。两种容器的 Prism 编程模型大体相同,但启动基类和具体容器 API 应以所选包版本为准。
MVVM:先把 View 和业务逻辑拆开
MVVM 不是 Prism 发明的,但 Prism 提供了很多配套工具。
- View:XAML 页面,负责布局和绑定;
- ViewModel:保存页面状态,暴露属性和命令;
- Model:业务数据和领域对象;
- Service:访问 API、数据库或系统能力。
ViewModel 不应该直接查找控件:
csharp
// 不推荐
var text = nameTextBox.Text;
nameTextBox.Text = "完成";
更适合通过属性和命令:
csharp
public string Name
{
get => name;
set => SetProperty(ref name, value);
}
public DelegateCommand SaveCommand { get; }
View 只负责绑定:
xml
<TextBox Text="{Binding Name, UpdateSourceTrigger=PropertyChanged}" />
<Button Content="保存" Command="{Binding SaveCommand}" />
这样 ViewModel 可以在没有 WPF 窗口的测试环境中运行,业务逻辑不依赖具体控件。
实战 Demo:模块化任务管理器
Demo 做一个简单的 WPF 任务管理器:
- Shell 提供左侧菜单和右侧内容区域;
- Dashboard 页面显示任务列表;
- Settings 页面显示任务新增状态;
- 使用 DryIoc 做依赖注入;
- 使用 Region Navigation 切换页面;
- 使用 EventAggregator 在页面之间传递任务新增事件;
- 通过命令代替按钮事件。
创建项目
shell
dotnet new wpf -n PrismTaskApp
cd PrismTaskApp
dotnet add package Prism.DryIoc
项目结构:
text
PrismTaskApp
├── App.xaml
├── App.xaml.cs
├── Views
│ ├── MainWindow.xaml
│ ├── DashboardView.xaml
│ └── SettingsView.xaml
├── ViewModels
│ ├── MainWindowViewModel.cs
│ ├── DashboardViewModel.cs
│ └── SettingsViewModel.cs
├── Events
│ └── TaskAddedEvent.cs
└── Services
├── ITaskService.cs
└── TaskService.cs
App.xaml:使用 PrismApplication
删除默认 StartupUri,改为 Prism 的应用根对象:
xml
<prism:PrismApplication
x:Class="PrismTaskApp.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:prism="http://prismlibrary.com/">
<Application.Resources />
</prism:PrismApplication>
App.xaml.cs:
csharp
using System.Windows;
using Prism.DryIoc;
using Prism.Ioc;
using Prism.Modularity;
using PrismTaskApp.Services;
using PrismTaskApp.ViewModels;
using PrismTaskApp.Views;
namespace PrismTaskApp;
public partial class App : PrismApplication
{
protected override Window CreateShell()
{
return Container.Resolve<MainWindow>();
}
protected override void RegisterTypes(
IContainerRegistry containerRegistry)
{
containerRegistry.RegisterSingleton<ITaskService, TaskService>();
containerRegistry.RegisterForNavigation<
DashboardView, DashboardViewModel>();
containerRegistry.RegisterForNavigation<
SettingsView, SettingsViewModel>();
}
protected override void ConfigureModuleCatalog(
IModuleCatalog moduleCatalog)
{
}
}
关键点:
- CreateShell 返回应用主窗口;
- RegisterTypes 注册服务和导航页面;
- Shell 创建时,容器会自动注入 ViewModel 依赖。
Shell:定义页面布局和 Region
Views/MainWindow.xaml:
xml
<Window
x:Class="PrismTaskApp.Views.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:prism="http://prismlibrary.com/"
Title="Prism 任务管理器"
Width="900"
Height="560">
<DockPanel>
<Border
DockPanel.Dock="Left"
Width="180"
Background="#F2F4F7">
<StackPanel Margin="12">
<TextBlock
Text="任务管理器"
FontSize="20"
FontWeight="Bold"
Margin="0,0,0,20" />
<Button
Content="任务面板"
Command="{Binding NavigateCommand}"
CommandParameter="DashboardView"
Margin="0,0,0,8" />
<Button
Content="设置"
Command="{Binding NavigateCommand}"
CommandParameter="SettingsView" />
</StackPanel>
</Border>
<ContentControl
prism:RegionManager.RegionName="ContentRegion"
Margin="18" />
</DockPanel>
</Window>
Views/MainWindow.xaml.cs:
csharp
namespace PrismTaskApp.Views;
public partial class MainWindow
{
public MainWindow()
{
InitializeComponent();
}
}
ContentControl 上的附加属性把它声明成名为 ContentRegion 的 Region。导航时,Prism 会把目标 View 放入这个区域。
Shell ViewModel:发起区域导航
ViewModels/MainWindowViewModel.cs:
csharp
using Prism.Commands;
using Prism.Mvvm;
using Prism.Regions;
namespace PrismTaskApp.ViewModels;
public sealed class MainWindowViewModel : BindableBase
{
private readonly IRegionManager regionManager;
public DelegateCommand<string> NavigateCommand { get; }
public MainWindowViewModel(IRegionManager regionManager)
{
this.regionManager = regionManager;
NavigateCommand = new DelegateCommand<string>(
Navigate,
viewName => !string.IsNullOrWhiteSpace(viewName));
}
private void Navigate(string? viewName)
{
if (string.IsNullOrWhiteSpace(viewName))
{
return;
}
regionManager.RequestNavigate(
"ContentRegion",
viewName);
}
}
DelegateCommand 把方法暴露给 XAML。RequestNavigate 接收 Region 名称和导航 URI;URI 默认使用注册时的 View 名称。
ViewModel 自动关联
Dashboard View 可以启用 ViewModel 自动注入:
xml
<UserControl
x:Class="PrismTaskApp.Views.DashboardView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:prism="http://prismlibrary.com/"
prism:ViewModelLocator.AutoWireViewModel="True">
<Grid>
<!-- 页面内容 -->
</Grid>
</UserControl>
默认命名约定:
text
Views/DashboardView.xaml
ViewModels/DashboardViewModel.cs
Prism 会尝试把 View 和同名 ViewModel 关联起来。命名不符合约定时,可以在导航注册中显式指定 ViewModel 类型。
服务注册与构造函数注入
Services/ITaskService.cs:
csharp
namespace PrismTaskApp.Services;
public interface ITaskService
{
IReadOnlyList<TaskItem> GetAll();
TaskItem Add(string title);
}
public sealed record TaskItem(
Guid Id,
string Title,
bool IsCompleted);
Services/TaskService.cs:
csharp
namespace PrismTaskApp.Services;
public sealed class TaskService : ITaskService
{
private readonly List<TaskItem> tasks =
[
new(Guid.NewGuid(), "整理需求", false),
new(Guid.NewGuid(), "编写单元测试", true)
];
public IReadOnlyList<TaskItem> GetAll()
{
return tasks.ToArray();
}
public TaskItem Add(string title)
{
var item = new TaskItem(
Guid.NewGuid(),
title,
false);
tasks.Add(item);
return item;
}
}
App 中注册:
csharp
containerRegistry.RegisterSingleton<ITaskService, TaskService>();
DashboardViewModel 通过构造函数拿到服务:
csharp
public DashboardViewModel(ITaskService taskService)
{
this.taskService = taskService;
}
ViewModel 不需要知道 DryIoc API,也不需要自己查找容器。测试时可以传入假的 ITaskService。
BindableBase 和 DelegateCommand
DashboardViewModel 的核心代码:
csharp
using System.Collections.ObjectModel;
using Prism.Commands;
using Prism.Events;
using Prism.Mvvm;
using PrismTaskApp.Events;
using PrismTaskApp.Services;
namespace PrismTaskApp.ViewModels;
public sealed class DashboardViewModel : BindableBase
{
private readonly ITaskService taskService;
private readonly IEventAggregator eventAggregator;
private string newTitle = string.Empty;
public ObservableCollection<TaskItem> Tasks { get; } = [];
public string NewTitle
{
get => newTitle;
set
{
if (SetProperty(ref newTitle, value))
{
AddTaskCommand.RaiseCanExecuteChanged();
}
}
}
public DelegateCommand AddTaskCommand { get; }
public DashboardViewModel(
ITaskService taskService,
IEventAggregator eventAggregator)
{
this.taskService = taskService;
this.eventAggregator = eventAggregator;
AddTaskCommand = new DelegateCommand(
AddTask,
() => !string.IsNullOrWhiteSpace(NewTitle));
foreach (var task in taskService.GetAll())
{
Tasks.Add(task);
}
}
private void AddTask()
{
var task = taskService.Add(NewTitle.Trim());
Tasks.Add(task);
eventAggregator
.GetEvent<TaskAddedEvent>()
.Publish(task);
NewTitle = string.Empty;
}
}
BindableBase.SetProperty 在值变化时触发 PropertyChanged,WPF Binding 才能刷新界面。修改属性后调用 RaiseCanExecuteChanged,按钮的可用状态也会随输入变化。
DashboardView.xaml:
xml
<UserControl
x:Class="PrismTaskApp.Views.DashboardView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:prism="http://prismlibrary.com/"
prism:ViewModelLocator.AutoWireViewModel="True">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<StackPanel Orientation="Horizontal">
<TextBox
Width="260"
Text="{Binding NewTitle,
UpdateSourceTrigger=PropertyChanged}" />
<Button
Content="添加任务"
Command="{Binding AddTaskCommand}"
Margin="8,0,0,0"
Padding="12,4" />
</StackPanel>
<ListBox
Grid.Row="1"
Margin="0,16,0,0"
ItemsSource="{Binding Tasks}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Title}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</UserControl>
按钮不处理 Click 事件。是否允许执行由 CanExecute 决定,具体动作由 AddTask 完成。命令可以被按钮、菜单项和快捷键复用。
EventAggregator:跨模块传递消息
定义事件 Events/TaskAddedEvent.cs:
csharp
using Prism.Events;
using PrismTaskApp.Services;
namespace PrismTaskApp.Events;
public sealed class TaskAddedEvent : PubSubEvent<TaskItem>
{
}
SettingsViewModel 订阅事件:
csharp
using Prism.Events;
using Prism.Mvvm;
using PrismTaskApp.Events;
using PrismTaskApp.Services;
namespace PrismTaskApp.ViewModels;
public sealed class SettingsViewModel : BindableBase
{
private string status = "还没有新增任务";
public string Status
{
get => status;
private set => SetProperty(ref status, value);
}
public SettingsViewModel(IEventAggregator eventAggregator)
{
eventAggregator
.GetEvent<TaskAddedEvent>()
.Subscribe(OnTaskAdded);
}
private void OnTaskAdded(TaskItem task)
{
Status = "最近新增:" + task.Title;
}
}
SettingsView.xaml:
xml
<UserControl
x:Class="PrismTaskApp.Views.SettingsView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:prism="http://prismlibrary.com/"
prism:ViewModelLocator.AutoWireViewModel="True">
<StackPanel>
<TextBlock Text="设置" FontSize="24" />
<TextBlock
Text="{Binding Status}"
Margin="0,12,0,0" />
</StackPanel>
</UserControl>
EventAggregator 适合传递"任务已新增""主题已切换"这类跨模块通知。不适合替代所有方法调用,也不适合承载必须有返回值的业务流程。
Region:动态 UI 的命名插槽
Region 可以理解为 Shell 里的命名插槽:
xml
<ContentControl
prism:RegionManager.RegionName="ContentRegion" />
模块或 Shell ViewModel 只需要知道 ContentRegion,不需要知道这个区域当前由哪个具体控件承载。
常见承载控件:
| 控件 | 适合场景 |
|---|---|
| ContentControl | 同一时间显示一个活动 View |
| TabControl | 多个 View 以标签页显示 |
| ItemsControl | 列表或多个 View 同时显示 |
Prism 通过 RegionAdapter 把 WPF 控件适配成 Region。默认提供 ContentControl、Selector 和 ItemsControl 的适配。自定义控件需要额外编写 RegionAdapter。
导航:View Injection、View Discovery 和 RequestNavigate
View Injection
代码直接把 View 加入 Region:
csharp
var region = regionManager.Regions["ContentRegion"];
region.Add(new DashboardView());
这种方式控制力强,但页面创建和 Region 名称会直接出现在代码里,适合需要手动管理多个 View 实例的场景。
View Discovery
注册某个区域和 View 的关系,区域创建时自动发现并加载:
csharp
regionViewRegistry.RegisterViewWithRegion(
"ContentRegion",
typeof(DashboardView));
适合固定的单实例区域。
Region Navigation
页面切换通常使用导航:
csharp
regionManager.RequestNavigate(
"ContentRegion",
"DashboardView");
导航时 Prism 会创建 View、ViewModel 和相关依赖,再把 View 放入 Region 并激活。
导航回调可以查看结果:
csharp
regionManager.RequestNavigate(
"ContentRegion",
"SettingsView",
result =>
{
if (!result.Success)
{
// 记录导航失败
}
});
导航参数和生命周期
传递参数:
csharp
var parameters = new NavigationParameters
{
{ "taskId", taskId }
};
regionManager.RequestNavigate(
"ContentRegion",
"TaskDetailView",
parameters);
接收参数的 ViewModel 可以实现 INavigationAware:
csharp
using Prism.Mvvm;
using Prism.Regions;
public sealed class TaskDetailViewModel : BindableBase,
INavigationAware
{
public void OnNavigatedTo(
NavigationContext navigationContext)
{
if (navigationContext.Parameters["taskId"] is Guid taskId)
{
// 根据 taskId 加载详情
}
}
public bool IsNavigationTarget(
NavigationContext navigationContext)
{
return true;
}
public void OnNavigatedFrom(
NavigationContext navigationContext)
{
// 保存草稿或释放页面级资源
}
}
IsNavigationTarget 返回 true 时,已有 View 实例可以被复用;返回 false 时,Prism 可以创建新的导航目标。页面有未保存内容时,可以实现导航确认接口,在离开前阻止或确认导航。
模块化:把功能拆成独立单元
模块适合按业务能力拆分:
text
PrismTaskApp
├── Shell
├── TaskModule
│ ├── Views
│ ├── ViewModels
│ └── Services
└── ReportModule
├── Views
├── ViewModels
└── Services
模块通过 IModule 注册自己的服务和导航页面:
csharp
using Prism.Ioc;
using Prism.Modularity;
public sealed class ReportModule : IModule
{
public void RegisterTypes(
IContainerRegistry containerRegistry)
{
containerRegistry.RegisterForNavigation<
ReportView, ReportViewModel>();
}
public void OnInitialized(
IContainerProvider containerProvider)
{
// 模块初始化逻辑
}
}
在 App 中配置模块:
csharp
protected override void ConfigureModuleCatalog(
IModuleCatalog moduleCatalog)
{
moduleCatalog.AddModule<ReportModule>();
}
按需加载:
csharp
moduleCatalog.AddModule<ReportModule>(
InitializationMode.OnDemand);
模块需要使用时,通过模块管理器加载:
csharp
public sealed class MainWindowViewModel : BindableBase
{
private readonly IModuleManager moduleManager;
public DelegateCommand LoadReportsCommand { get; }
public MainWindowViewModel(IModuleManager moduleManager)
{
this.moduleManager = moduleManager;
LoadReportsCommand = new DelegateCommand(
() => moduleManager.LoadModule("ReportModule"));
}
}
模块化的关键不是把文件放进不同文件夹,而是模块拥有自己的注册、视图和服务,Shell 不依赖模块内部实现。
Dialog Service:以 MVVM 方式显示对话框
直接在 ViewModel 中创建 Window,会把具体 UI 类型带进业务层。Prism Dialog Service 可以把对话框请求放在 ViewModel 中。
对话框 ViewModel 通常实现 IDialogAware:
csharp
using Prism.Mvvm;
using Prism.Services.Dialogs;
public sealed class ConfirmDialogViewModel :
BindableBase, IDialogAware
{
public string Title => "确认操作";
// Prism 9 使用 DialogCloseListener;Prism 8 及更早版本使用关闭事件。
public DialogCloseListener RequestClose { get; }
public bool CanCloseDialog() => true;
public void OnDialogClosed()
{
}
public void OnDialogOpened(IDialogParameters parameters)
{
}
public void Confirm()
{
RequestClose.Invoke(ButtonResult.Yes);
}
}
启动时注册对话框:
csharp
containerRegistry.RegisterDialog<
ConfirmDialog, ConfirmDialogViewModel>();
调用时通过 IDialogService 打开 ConfirmDialog,结果由回调接收。Prism 8 或更早版本的项目仍可能使用 event Action<IDialogResult> RequestClose 写法,升级 Prism 时需要按目标版本调整这一段代码。
核心流程:
text
ViewModel 发起对话框请求
↓
Dialog Service 创建 View
↓
对话框 ViewModel 处理结果
↓
DialogResult 返回结果
复杂输入对话框应有明确的参数、返回值和关闭条件,不要把所有窗口都包装成无规则的全局弹窗服务。
Prism 中的依赖注入
常见注册:
csharp
containerRegistry.Register<IClock, SystemClock>();
containerRegistry.RegisterSingleton<IAppState, AppState>();
containerRegistry.RegisterForNavigation<DashboardView>();
常见生命周期:
| 注册方式 | 含义 |
|---|---|
| Register | 按容器默认规则创建实例 |
| RegisterSingleton | 整个容器共享一个实例 |
| RegisterInstance | 注册已创建好的实例 |
| RegisterForNavigation | 注册 View 和导航名称 |
不要在 ViewModel 中直接保存 IContainerProvider 再到处 Resolve。构造函数注入更容易阅读、测试和发现依赖;容器访问应主要集中在 App、模块和基础设施边界。
WPF Prism 和 .NET MAUI Prism 不要混用
Prism 支持多个 XAML 平台,但 API 不是完全相同:
| 平台 | 常见包 | 主要导航模型 |
|---|---|---|
| WPF | Prism.DryIoc、Prism.Unity | Region、IRegionManager |
| .NET MAUI | Prism.Maui、Prism.DryIoc.Maui | 页面导航、Shell |
| Avalonia | Prism.DryIoc.Avalonia | 平台对应实现 |
| Uno / WinUI | 对应 Prism 平台包 | 平台对应实现 |
WPF 的 Region 是桌面复合界面的重要概念;MAUI 更常见的是页面栈和 URI 页面导航。跨平台共享 ViewModel、服务和事件模型可以,但 XAML View、窗口类型和导航代码通常需要分平台。
常见错误
ViewModel 直接操作控件
ViewModel 不应依赖 TextBox、Window 和 ListBox。通过属性、命令、服务和事件传递状态。
注册了 View,却没有注册导航
RequestNavigate 前,需要确保目标 View 已使用 RegisterForNavigation 注册,或者使用了正确的 View Discovery 配置。
Region 名称写错
Region 名称是字符串协议:
text
XAML:ContentRegion
C#:contentRegion
大小写不一致就可能导致导航失败。可以集中定义区域名称常量,减少散落字符串。
用 EventAggregator 代替所有服务调用
事件没有明确返回值,调用关系也不直观。查询和有结果的操作优先使用接口服务。
启动阶段加载所有模块
模块数量较大时,启动加载会拖慢首屏。可以按功能按需加载,但必须处理加载失败、依赖缺失和首次打开延迟。
单例服务保存临时页面状态
WPF 应用中的 Singleton 会贯穿整个进程生命周期。窗口临时状态、当前编辑对象和用户操作上下文不应随意放进全局单例。
忽略 UI 线程
后台任务完成后直接修改 ObservableCollection,可能触发跨线程异常。需要通过 Dispatcher 或项目约定的 UI 调度机制回到 UI 线程。
只用绑定错误猜问题
定位绑定问题时,同时检查 DataContext、ViewModel 构造、属性通知、命令 CanExecute 和 XAML 路径。
什么时候适合使用 Prism
Prism 适合:
- 企业级 WPF 客户端;
- ERP、MES、CRM、工控和医疗桌面软件;
- 多模块、多团队维护的 XAML 应用;
- 需要区域组合、模块按需加载和统一导航的客户端;
- 希望 ViewModel 可以独立测试的项目。
不一定需要 Prism 的情况:
- 只有几个页面的小工具;
- WPF 默认 MVVM 加简单 DI 已经足够;
- 项目不需要模块化、区域导航和跨模块通信;
- 团队不熟悉 XAML、MVVM 和容器,框架反而会增加维护成本。
总结
Prism 的核心不是某一个基类,而是一套组织复杂 XAML 应用的方式:
text
PrismApplication
↓
DI 容器 + Shell
↓
Region 组合界面
↓
模块提供 View 和服务
↓
ViewModel 通过命令和属性驱动 UI
↓
EventAggregator 传递跨模块通知
实际开发可以按这个顺序落地:
- 用 BindableBase 和 DelegateCommand 把事件逻辑移到 ViewModel;
- 用构造函数注入服务,避免 ViewModel 自己创建依赖;
- 用 Region 和导航管理页面切换;
- 用 Module 拆分独立业务功能;
- 用 EventAggregator 处理少量跨模块通知;
- 对话框、导航参数和页面生命周期建立明确约定;
- 模块增多后再考虑按需加载和插件目录。
WPF 使用 Prism.DryIoc 或 Prism.Unity,MAUI 使用对应的 Prism.Maui 包;平台 API 不应混用。掌握 View、ViewModel、Region、Module、Navigation 和 EventAggregator 后,Prism 的日常开发就有了清晰的落点。
参考资料: