Prism 框架 入门

📅 2026/7/12 19:15:37 👁️ 阅读次数 📝 编程学习
Prism 框架 入门

一.定义

Prism 是微软 Patterns & Practices 团队推出的模块化 MVVM 框架,专门用于 WPF/MAUI/Uno,主打大型桌面 / 移动端分层解耦开发

特点:

  • 模块化:大型项目拆分多个独立模块(业务插件),按需加载、团队并行开发
  • 依赖注入 DI:统一管理服务、ViewModel,不用手动 new 对象
  • 区域导航 Region:页面容器自动管理视图切换(侧边栏、主内容区、弹窗统一管理)
  • 事件聚合器 EventAggregator:模块之间解耦通信,不用强引用
  • MVVM 标准化:内置 BindableBase、DelegateCommand,不用自己实现 INotifyPropertyChanged/ICommand

阶段一 手写入门

DI容器

第一种:你自己写的业务类 → 必须手动注册,否则无法注入

第二种:Prism 框架自带内置服务 → 已经自动注册,直接注入不用管

  • IRegionManager(区域管理器,你一直在用)
  • IEventAggregator(事件总线)
  • IDialogService(弹窗服务)
  • INavigationService(导航服务)

运行顺序

步骤 1:在容器中注册你的页面(App.xaml.cs)

步骤 2:在界面上挖好坑,并命名区域(MainWindow.xaml)

步骤 3:构造函数注入区域管理器(MainWindow.xaml.cs)

步骤 4:呼叫管家,下达跳转命令(导航触发)

步骤 1:改造 App.xaml + App.xaml.cs(Prism 入口)

原生 WPF 入口是Application,Prism 必须继承PrismApplication,负责:DI 容器初始化、注册服务、启动主窗口 Shell。

App.xaml(删除原有 StartupUri)

<prism:PrismApplication x:Class="PrismDemo.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> </Application.Resources> </prism:PrismApplication>

App.xaml.cs

using Prism; using Prism.Ioc; using PrismDemo.Views; using System.Windows; namespace PrismDemo { public partial class App : PrismApplication { // 1. 创建主窗口(Shell:整个程序根窗口) protected override Window CreateShell() { // 从DI容器拿Shell窗口,不要new return Container.Resolve<MainShell>(); } // 2. 类型注册:注册窗口、视图、服务到DI容器 protected override void RegisterTypes(IContainerRegistry containerRegistry) { // 注册主窗口 containerRegistry.RegisterWindow<MainShell>(); // 注册页面(后面Region导航用) containerRegistry.RegisterForNavigation<HomeView>(); // 注册全局服务(单例) containerRegistry.RegisterSingleton<IMessageService, MessageService>(); } } }

步骤 2:创建根窗口 MainShell.xaml(Shell,程序主容器)

Shell 是整个程序的根窗口,定义 Region 区域(用来放页面切换)

<Window x:Class="PrismDemo.Views.MainShell" 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入门Demo" Height="450" Width="800"> <Grid> <!-- Prism区域:Name就是Region名称,用来导航跳转页面 --> <ContentControl prism:RegionManager.RegionName="MainRegion"/> </Grid> </Window>

阶段二 Prism的内置MVVM体系

问题:

我们手写原生 MVVM 时,每个 ViewModel 都要重复实现INotifyPropertyChanged接口、手写属性通知、手动实现ICommand命令,模板代码多、容易出错

Prism 已经把这些重复逻辑全部封装成了现成的基类和工具类,我们只需要继承 / 调用,就能快速写出标准 MVVM 代码,专注业务逻辑。

1. ObservableObject:属性通知基类

1.1 它解决什么问题

原生 MVVM 中,每个属性都要手写「字段判等 → 赋值 → 触发通知」三步,重复度极高。ObservableObject是 Prism 提供的 ViewModel 基类,它内部已经实现了INotifyPropertyChanged接口,并封装了SetProperty方法,一行代码完成属性赋值 + 自动通知。

命名空间:Prism.Mvvm

1.2 手写 vs Prism 写法对比

原生手写属性(繁琐)

// 还要自己实现整个 INotifyPropertyChanged 接口 private string _userName; public string UserName { get => _userName; set { if (_userName != value) { _userName = value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(UserName))); } } }

Prism ObservableObject 写法(简洁)

// ViewModel 只需要继承 ObservableObject public class MainViewModel : ObservableObject { private string _userName; // set 里直接调用 SetProperty public string UserName { get => _userName; set => SetProperty(ref _userName, value); } }

1.3 SetProperty 内部做了什么

SetProperty(ref 字段, 新值)自动完成 3 件事:

  1. 值判等:新旧值相同则什么都不做,避免无效的 UI 刷新
  2. 字段赋值:值不同则给私有字段赋新值
  3. 触发通知:赋值后自动触发PropertyChanged事件,UI 自动更新

1.4 进阶:赋值后执行额外逻辑

如果属性变更后需要追加操作(比如联动刷新命令状态),可用带回调的重载:

private string _password; public string Password { get => _password; set => SetProperty(ref _password, value, () => { // 密码变更后,自动刷新登录按钮的可用状态 LoginCommand.RaiseCanExecuteChanged(); }); }

2. DelegateCommand 与 AsyncDelegateCommand:命令封装

2.1 前置认知:ICommand 的本质

WPF 命令必须实现ICommand接口,核心是 3 个成员:

  • Execute:命令执行的业务逻辑
  • CanExecute:返回 bool,控制命令是否可执行(按钮灰 / 可用)
  • CanExecuteChanged:通知系统重新检查命令可用状态

Prism 把接口实现全部封装好了,同步用DelegateCommand,异步用AsyncDelegateCommand

命名空间:Prism.Commands

2.2 DelegateCommand:同步命令

基础无参数命令

// 1. 定义命令属性 public DelegateCommand SayHelloCommand { get; } // 2. 构造函数中初始化,传入执行方法 public MainViewModel() { SayHelloCommand = new DelegateCommand(SayHello); } // 3. 命令执行的业务方法 private void SayHello() { MessageBox.Show("Hello Prism!"); }

XAML 绑定和原生命令完全一致:

<Button Content="打招呼" Command="{Binding SayHelloCommand}" />
带 CanExecute 的命令

控制按钮可用状态,比如「保存」按钮只有内容修改后才能点击

public DelegateCommand SaveCommand { get; } private bool _isModified; public bool IsModified { get => _isModified; set => SetProperty(ref _isModified, value); } public MainViewModel() { // 第二个参数传入 CanExecute 判断方法 SaveCommand = new DelegateCommand(Save, CanSave); } private void Save() { /* 保存逻辑 */ } // 返回 true 按钮可用,false 按钮禁用 private bool CanSave() => IsModified;

注意:原生写法中,IsModified变化后,按钮不会自动刷新状态,需要手动调用SaveCommand.RaiseCanExecuteChanged()

ObservesProperty:自动刷新命令状态

Prism 提供ObservesProperty方法,监听指定属性,属性一变就自动刷新 CanExecute,不用手动调用。

public MainViewModel() { SaveCommand = new DelegateCommand(Save, CanSave) .ObservesProperty(() => IsModified); // 监听 IsModified 属性 } //支持链式监听多个属性: LoginCommand = new DelegateCommand(Login, CanLogin) .ObservesProperty(() => UserName) .ObservesProperty(() => Password);
泛型命令 DelegateCommand<T>

如果需要给命令传参数(对应 XAML 的CommandParameter),用泛型版本。

// 指定参数类型为 int public DelegateCommand<int> DeleteCommand { get; } public MainViewModel() { DeleteCommand = new DelegateCommand<int>(Delete); } private void Delete(int id) { // 根据 id 执行删除逻辑 }

XAML 传参:

<Button Content="删除" Command="{Binding DeleteCommand}" CommandParameter="1001" />

2.3 AsyncDelegateCommand:异步命令

如果给DelegateCommandasync void方法,会有两个问题:

  • 异常无法被正常捕获,容易导致程序崩溃
  • 无法跟踪执行状态,不能防止重复点击

AsyncDelegateCommand是 Prism 专门为异步场景封装的,支持async Task方法,还自带IsExecuting执行状态属性。

public AsyncDelegateCommand LoadDataCommand { get; } public MainViewModel() { LoadDataCommand = new AsyncDelegateCommand(LoadDataAsync); } // 执行方法是 async Task,不是 async void private async Task LoadDataAsync() { await Task.Delay(2000); // 模拟耗时加载 // 加载完成后的业务逻辑 }
防止重复点击(常用技巧)

异步任务执行期间自动禁用按钮,执行完自动恢复:

public MainViewModel() { LoadDataCommand = new AsyncDelegateCommand(LoadDataAsync) .ObservesCanExecute(() => IsNotLoading); } // 执行中 IsExecuting=true → IsNotLoading=false → 按钮自动禁用 private bool IsNotLoading => !LoadDataCommand.IsExecuting;

3.CompositeCommand:复合命令(批量操作神器)

3.1应用场景

上位机开发高频使用:比如主界面「全部启动」按钮,点击后同时启动设备 A、设备 B、设备 C 的启动命令;还有「全部停止」「批量初始化」等场景。

核心逻辑:复合命令是一个「命令容器」,多个子命令注册进去后,触发复合命令时,所有子命令一起执行

3.2 实操步骤

第一步:定义全局复合命令

通常放在静态类中,方便各个子模块访问

public static class GlobalCommands { // 全局「全部启动」复合命令 public static CompositeCommand AllStartCommand { get; } = new CompositeCommand(true); // 传 true 表示监控子命令状态,子命令变了自动刷新复合命令可用状态 }
第二步:子 ViewModel 注册命令

比如设备 A 的 ViewModel 把自己的启动命令注册进去:

public class DeviceAViewModel : ObservableObject { public DelegateCommand StartCommand { get; } public DeviceAViewModel() { StartCommand = new DelegateCommand(Start); // 注册到全局复合命令 GlobalCommands.AllStartCommand.RegisterCommand(StartCommand); } private void Start() { // 设备A启动逻辑 } }
第三步:主界面绑定复合命令
<Button Content="全部启动" Command="{x:Static local:GlobalCommands.AllStartCommand}" />

3.3 注意事项

  • ViewModel 销毁 / 页面关闭时,调用UnregisterCommand注销命令,避免内存泄漏
  • 默认规则:所有子命令都可执行时,复合命令才可执行

4. View 与 ViewModel 自动绑定(命名约定)

4.1 作用

MVVM 中 View 需要通过DataContext关联 ViewModel 才能绑定数据。如果每个页面都手动写DataContext = new XXXViewModel(),耦合度高,也不支持依赖注入。

Prism 提供ViewModelLocator(视图模型定位器),按照命名约定自动找到对应 ViewModel 并赋值DataContext

4.2 默认命名约定

遵循「约定大于配置」:

  • View 放在Views文件夹,类名以View结尾,如MainView.xaml
  • ViewModel 放在ViewModels文件夹,类名以ViewModel结尾,如MainViewModel
  • 自动配对:MainViewMainViewModel

4.3 开启自动绑定

在 View 的 XAML 中添加附加属性即可:

  1. 引入 Prism 命名空间
  2. 开启自动绑定
<Window x:Class="PrismDemo.Views.MainView" 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" Title="主窗口" Height="450" Width="800"> <Grid> <TextBox Text="{Binding UserName}" /> </Grid> </Window>

运行后 Prism 会自动创建MainViewModel实例,赋值给当前 View 的DataContext

5. 手动绑定 ViewModel 的两种方式

自动绑定是主流方案,特殊场景(打破命名约定、自定义注入逻辑)需要手动绑定,常用两种方式:

方式一:构造函数注入(依赖注入方式)

Prism 自带依赖注入容器,ViewModel 会被容器管理。在 View 的构造函数中直接注入 ViewModel 实例,再赋值给DataContext

public partial class MainView : Window { public MainView(MainViewModel viewModel) { InitializeComponent(); // 把容器注入的 ViewModel 赋值给 DataContext DataContext = viewModel; } }

优点:强类型、显式可控,适合需要在 View 构造中做额外处理的场景。

方式二:ViewModelLocator 手动指定类型

不想遵守命名约定时,可强制指定 View 对应的 ViewModel 类型。

<Window x:Class="YourApp.Views.CustomView" xmlns:prism="http://prismlibrary.com/" xmlns:vm="clr-namespace:YourApp.ViewModels" prism:ViewModelLocator.AutoWireViewModel="True" prism:ViewModelLocator.ViewModelType="{x:Type vm:MyCustomViewModel}">