别把 WPF 写成一个 MainWindow:C#.NET Prism 模块化开发详解

简介

WPF 项目刚开始时,窗口、按钮和业务代码放在一起,开发速度很快:

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 常见结构:

MainWindow.xaml
MainWindow.xaml.cs
    ├── 查询数据
    ├── 保存数据
    ├── 创建服务
    ├── 打开子窗口
    └── 更新其他控件

Prism 更推荐:

View
  ↓ Binding / Command
ViewModel
  ↓ 构造函数注入
Application Service
  ↓
Repository / API Client

大型客户端还可以继续拆成模块:

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 常用:

dotnet add package Prism.DryIoc

也可以选择:

dotnet add package Prism.Unity

本文 Demo 使用 DryIoc。两种容器的 Prism 编程模型大体相同,但启动基类和具体容器 API 应以所选包版本为准。

MVVM:先把 View 和业务逻辑拆开

MVVM 不是 Prism 发明的,但 Prism 提供了很多配套工具。

  • View:XAML 页面,负责布局和绑定;
  • ViewModel:保存页面状态,暴露属性和命令;
  • Model:业务数据和领域对象;
  • Service:访问 API、数据库或系统能力。

ViewModel 不应该直接查找控件:

// 不推荐
var text = nameTextBox.Text;
nameTextBox.Text = "完成";

更适合通过属性和命令:

public string Name
{
    get => name;
    set => SetProperty(ref name, value);
}

public DelegateCommand SaveCommand { get; }

View 只负责绑定:

<TextBox Text="{Binding Name, UpdateSourceTrigger=PropertyChanged}" />
<Button Content="保存" Command="{Binding SaveCommand}" />

这样 ViewModel 可以在没有 WPF 窗口的测试环境中运行,业务逻辑不依赖具体控件。

实战 Demo:模块化任务管理器

Demo 做一个简单的 WPF 任务管理器:

  • Shell 提供左侧菜单和右侧内容区域;
  • Dashboard 页面显示任务列表;
  • Settings 页面显示任务新增状态;
  • 使用 DryIoc 做依赖注入;
  • 使用 Region Navigation 切换页面;
  • 使用 EventAggregator 在页面之间传递任务新增事件;
  • 通过命令代替按钮事件。

创建项目

dotnet new wpf -n PrismTaskApp
cd PrismTaskApp
dotnet add package Prism.DryIoc

项目结构:

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 的应用根对象:

<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:

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:

<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:

namespace PrismTaskApp.Views;

public partial class MainWindow
{
    public MainWindow()
    {
        InitializeComponent();
    }
}

ContentControl 上的附加属性把它声明成名为 ContentRegion 的 Region。导航时,Prism 会把目标 View 放入这个区域。

Shell ViewModel:发起区域导航

ViewModels/MainWindowViewModel.cs:

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 自动注入:

<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>

默认命名约定:

Views/DashboardView.xaml
ViewModels/DashboardViewModel.cs

Prism 会尝试把 View 和同名 ViewModel 关联起来。命名不符合约定时,可以在导航注册中显式指定 ViewModel 类型。

服务注册与构造函数注入

Services/ITaskService.cs:

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:

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 中注册:

containerRegistry.RegisterSingleton<ITaskService, TaskService>();

DashboardViewModel 通过构造函数拿到服务:

public DashboardViewModel(ITaskService taskService)
{
    this.taskService = taskService;
}

ViewModel 不需要知道 DryIoc API,也不需要自己查找容器。测试时可以传入假的 ITaskService。

BindableBase 和 DelegateCommand

DashboardViewModel 的核心代码:

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:

<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:

using Prism.Events;
using PrismTaskApp.Services;

namespace PrismTaskApp.Events;

public sealed class TaskAddedEvent : PubSubEvent<TaskItem>
{
}

SettingsViewModel 订阅事件:

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:

<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 里的命名插槽:

<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:

var region = regionManager.Regions["ContentRegion"];
region.Add(new DashboardView());

这种方式控制力强,但页面创建和 Region 名称会直接出现在代码里,适合需要手动管理多个 View 实例的场景。

View Discovery

注册某个区域和 View 的关系,区域创建时自动发现并加载:

regionViewRegistry.RegisterViewWithRegion(
    "ContentRegion",
    typeof(DashboardView));

适合固定的单实例区域。

Region Navigation

页面切换通常使用导航:

regionManager.RequestNavigate(
    "ContentRegion",
    "DashboardView");

导航时 Prism 会创建 View、ViewModel 和相关依赖,再把 View 放入 Region 并激活。

导航回调可以查看结果:

regionManager.RequestNavigate(
    "ContentRegion",
    "SettingsView",
    result =>
    {
        if (!result.Success)
        {
            // 记录导航失败
        }
    });

导航参数和生命周期

传递参数:

var parameters = new NavigationParameters
{
    { "taskId", taskId }
};

regionManager.RequestNavigate(
    "ContentRegion",
    "TaskDetailView",
    parameters);

接收参数的 ViewModel 可以实现 INavigationAware:

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 可以创建新的导航目标。页面有未保存内容时,可以实现导航确认接口,在离开前阻止或确认导航。

模块化:把功能拆成独立单元

模块适合按业务能力拆分:

PrismTaskApp
├── Shell
├── TaskModule
│   ├── Views
│   ├── ViewModels
│   └── Services
└── ReportModule
    ├── Views
    ├── ViewModels
    └── Services

模块通过 IModule 注册自己的服务和导航页面:

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 中配置模块:

protected override void ConfigureModuleCatalog(
    IModuleCatalog moduleCatalog)
{
    moduleCatalog.AddModule<ReportModule>();
}

按需加载:

moduleCatalog.AddModule<ReportModule>(
    InitializationMode.OnDemand);

模块需要使用时,通过模块管理器加载:

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:

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);
    }
}

启动时注册对话框:

containerRegistry.RegisterDialog<
    ConfirmDialog, ConfirmDialogViewModel>();

调用时通过 IDialogService 打开 ConfirmDialog,结果由回调接收。Prism 8 或更早版本的项目仍可能使用 event Action<IDialogResult> RequestClose 写法,升级 Prism 时需要按目标版本调整这一段代码。

核心流程:

ViewModel 发起对话框请求
        ↓
Dialog Service 创建 View
        ↓
对话框 ViewModel 处理结果
        ↓
DialogResult 返回结果

复杂输入对话框应有明确的参数、返回值和关闭条件,不要把所有窗口都包装成无规则的全局弹窗服务。

Prism 中的依赖注入

常见注册:

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 名称是字符串协议:

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 应用的方式:

PrismApplication
    ↓
DI 容器 + Shell
    ↓
Region 组合界面
    ↓
模块提供 View 和服务
    ↓
ViewModel 通过命令和属性驱动 UI
    ↓
EventAggregator 传递跨模块通知

实际开发可以按这个顺序落地:

  1. 用 BindableBase 和 DelegateCommand 把事件逻辑移到 ViewModel;
  2. 用构造函数注入服务,避免 ViewModel 自己创建依赖;
  3. 用 Region 和导航管理页面切换;
  4. 用 Module 拆分独立业务功能;
  5. 用 EventAggregator 处理少量跨模块通知;
  6. 对话框、导航参数和页面生命周期建立明确约定;
  7. 模块增多后再考虑按需加载和插件目录。

WPF 使用 Prism.DryIoc 或 Prism.Unity,MAUI 使用对应的 Prism.Maui 包;平台 API 不应混用。掌握 View、ViewModel、Region、Module、Navigation 和 EventAggregator 后,Prism 的日常开发就有了清晰的落点。

参考资料:

posted on 2026-09-23 17:54  我是唐青枫  阅读(2)  评论(0)    收藏  举报