C# WPF 的 Prism 框架入门:核心概念与一个完整示例

C# WPF 的 Prism 框架入门:核心概念与一个完整示例

前言

做过一段时间 WPF 开发的都知道,直接用"事件 + 代码后置(Code-Behind)"写界面,项目一大就容易失控:View 和逻辑耦合在一起、页面之间跳转要靠手动维护窗口、模块之间互相引用、单元测试难写。Prism 就是为解决这些问题而生的一个成熟 MVVM 框架。

本文面向 Prism 新手,先讲清楚它是什么、解决什么问题,再手把手带你搭一个能跑起来的完整示例(包含 Shell、区域、视图、ViewModel、命令和导航)。

一、什么是 Prism

Prism(原名 Prism Library / Composite Application Guidance)是由微软 Patterns & Practices 团队发起、现由社区维护的开源框架,专为 WPF、Xamarin.Forms、MAUI 等 XAML 技术栈提供 MVVM 与模块化支持。

它主要解决两类问题:

  1. 应用结构:用 MVVM 把界面(View)与逻辑(ViewModel)解耦,代码可测试、可维护。
  2. 大型应用拆分:通过"模块化(Modularity)"和"区域(Region)",把一个大应用拆成可独立开发、独立加载的模块,页面可以动态组合。

Prism 的核心价值可以概括为一句话:让你专注写业务,框架帮你管结构和生命周期

二、Prism 核心概念速览

先用一张表快速认识 Prism 的各个子系统(后续文章会逐一深入):

  1. Bootstrapper(引导) :继承 PrismApplication,负责创建依赖注入容器、注册类型、创建主窗口(Shell)、加载模块,是应用的"启动入口"。
  2. 依赖注入(DI):内置容器(DryIoc 或 Unity),对象由容器创建和注入,天然解耦。
  3. Shell 与 Region(区域):Shell 是主窗口,里面定义若干"命名区域",其他视图可以动态注入到这些区域中。
  4. ViewModelLocator(视图模型定位) :按约定(HomeViewHomeViewModel)自动给 View 绑定 ViewModel。
  5. BindableBase :实现 INotifyPropertyChanged 的基类,用 SetProperty 写属性变更通知。
  6. DelegateCommand(命令) :实现 ICommand 的命令,替代事件处理,支持 CanExecute
  7. Navigation(导航)IRegionManager.RequestNavigate 在区域内切换视图,支持传参、回退。
  8. EventAggregator(事件聚合):模块间松耦合通信,类似发布/订阅。
  9. DialogService(对话框) :以 MVVM 方式弹窗,不直接 new Window

本文示例会用到其中的 1、2、3、4、5、6、7 七项,足够你入门。

三、环境搭建

3.1 创建项目

  1. 用 Visual Studio 新建一个 WPF 应用(建议目标框架选 .NET 8,Prism 9.x 支持;.NET 6 可用 Prism 8.1)。
  2. 项目创建好后,默认是一个 App.xaml + MainWindow.xaml 的传统结构。

3.2 安装 Prism

在 NuGet 包管理器安装:

  1. Prism.DryIoc(推荐,Prism 8.1+ / 9.x 的默认容器)。
  2. 或者 Prism.Unity(Prism 8.x 老项目常用;Prism 9 已移除 Unity 支持)。

安装后会自动引入 Prism.CorePrism.Wpf 等依赖。本文以 Prism.DryIoc 为例。

四、应用启动流程(Bootstrapper)

Prism 应用与传统 WPF 应用最大的区别在于 App.xamlApp.xaml.cs

4.1 App.xaml

根元素不再是 <Application>,而是 prism:PrismApplication,并且不要StartupUri(主窗口由 Prism 自己创建):

xml 复制代码
<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>

4.2 App.xaml.cs

继承 PrismApplication,实现两个核心方法:

csharp 复制代码
using Prism.Ioc;
using Prism.Regions;
using Prism.DryIoc;   // 若用 Unity 则为 Prism.Unity
using System.Windows;
using PrismDemo.Views;

namespace PrismDemo
{
    public partial class App : PrismApplication
    {
        // 创建并返回主窗口(Shell)
        protected override Window CreateShell()
        {
            return Container.Resolve<MainWindow>();
        }

        // 注册服务与可导航的视图
        protected override void RegisterTypes(IContainerRegistry containerRegistry)
        {
            containerRegistry.RegisterForNavigation<HomeView>();
            containerRegistry.RegisterForNavigation<DetailView>();
        }

        // 初始化完成后,做一次初始导航,让 ContentRegion 显示首页
        protected override void OnInitialized()
        {
            base.OnInitialized();
            var regionManager = Container.Resolve<IRegionManager>();
            regionManager.RequestNavigate("ContentRegion", nameof(HomeView));
        }
    }
}

要点说明:

  1. CreateShell:返回主窗口,Prism 会自动 Show() 它。
  2. RegisterTypes:注册服务、以及用 RegisterForNavigation<T>() 注册可导航视图。
  3. OnInitialized:初始化完成后执行,这里做初始导航。

五、核心用法讲解

5.1 Shell 与 Region

Shell 是应用的外壳窗口,里面放置若干"区域"。区域是一个占位符(ContentControlItemsControlTabControl 等),通过附加属性 RegionManager.RegionName 命名,之后视图可以注入到区域中:

xml 复制代码
<Window x:Class="PrismDemo.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 入门示例" Height="450" Width="700">
    <Grid>
        <!-- 一个名为 ContentRegion 的内容区域,导航的内容会显示在这里 -->
        <ContentControl prism:RegionManager.RegionName="ContentRegion"/>
    </Grid>
</Window>

5.2 View 与 ViewModel 自动绑定

在 View 的根元素上加 prism:ViewModelLocator.AutoWireViewModel="True",Prism 会按约定自动查找并绑定 ViewModel:

  • 约定:Views.HomeViewViewModels.HomeViewModel(把 "Views" 替换成 "ViewModels","View" 后缀替换成 "ViewModel")。

5.3 BindableBase

ViewModel 继承 BindableBase,用 SetProperty 写属性,自动触发属性变更通知:

csharp 复制代码
private string _message = "欢迎使用 Prism";
public string Message
{
    get => _message;
    set => SetProperty(ref _message, value);
}

5.4 DelegateCommand

用命令替代事件处理,命令在 ViewModel 构造时创建:

csharp 复制代码
public DelegateCommand GreetCommand { get; }

public HomeViewModel(IRegionManager regionManager)
{
    GreetCommand = new DelegateCommand(OnGreet);
}

private void OnGreet()
{
    // 业务逻辑
}

在 XAML 中通过 Command="{Binding GreetCommand}" 绑定按钮。

IRegionManager.RequestNavigate(区域名, 视图名, 参数) 在区域内切换视图,参数用 NavigationParameters 传递:

csharp 复制代码
var parameters = new NavigationParameters { { "name", InputName } };
_regionManager.RequestNavigate("ContentRegion", nameof(DetailView), parameters);

接收方 ViewModel 实现 INavigationAware 接口,在 OnNavigatedTo 里取参数。

六、完整示例:一个带导航的小应用

下面给出全部源码。功能:主页输入名字,点"打招呼"用命令显示问候;点"去详情页"导航到详情页并带上名字,详情页可"返回主页"。

6.1 项目结构

text 复制代码
PrismDemo/
├─ App.xaml
├─ App.xaml.cs
├─ Views/
│  ├─ MainWindow.xaml          (Shell)
│  ├─ MainWindow.xaml.cs
│  ├─ HomeView.xaml
│  ├─ HomeView.xaml.cs
│  ├─ DetailView.xaml
│  └─ DetailView.xaml.cs
└─ ViewModels/
   ├─ HomeViewModel.cs
   └─ DetailViewModel.cs

6.2 Shell:MainWindow.xaml

xml 复制代码
<Window x:Class="PrismDemo.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 入门示例" Height="450" Width="700">
    <Grid>
        <ContentControl prism:RegionManager.RegionName="ContentRegion"/>
    </Grid>
</Window>
csharp 复制代码
// MainWindow.xaml.cs
using System.Windows;

namespace PrismDemo.Views
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }
    }
}

6.3 主页:HomeView + HomeViewModel

xml 复制代码
<!-- HomeView.xaml -->
<UserControl x:Class="PrismDemo.Views.HomeView"
             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 Margin="24">
        <TextBlock Text="Prism 入门示例" FontSize="26" FontWeight="Bold" Margin="0,0,0,20"/>
        <StackPanel Orientation="Horizontal" Margin="0,0,0,12">
            <TextBlock Text="请输入名字:" VerticalAlignment="Center" FontSize="14"/>
            <TextBox Text="{Binding InputName, UpdateSourceTrigger=PropertyChanged}" Width="220" FontSize="14"/>
        </StackPanel>
        <StackPanel Orientation="Horizontal">
            <Button Content="打招呼" Command="{Binding GreetCommand}" Width="90" Margin="0,0,10,0"/>
            <Button Content="去详情页" Command="{Binding GoDetailCommand}" Width="90"/>
        </StackPanel>
        <TextBlock Text="{Binding Message}" FontSize="16" Foreground="Green" Margin="0,24,0,0" TextWrapping="Wrap"/>
    </StackPanel>
</UserControl>
csharp 复制代码
// HomeView.xaml.cs
using System.Windows.Controls;

namespace PrismDemo.Views
{
    public partial class HomeView : UserControl
    {
        public HomeView()
        {
            InitializeComponent();
        }
    }
}
csharp 复制代码
// HomeViewModel.cs
using System;
using Prism.Commands;
using Prism.Mvvm;
using Prism.Regions;
using PrismDemo.Views;

namespace PrismDemo.ViewModels
{
    public class HomeViewModel : BindableBase
    {
        private readonly IRegionManager _regionManager;

        private string _inputName = string.Empty;
        public string InputName
        {
            get => _inputName;
            set => SetProperty(ref _inputName, value);
        }

        private string _message = "欢迎使用 Prism";
        public string Message
        {
            get => _message;
            set => SetProperty(ref _message, value);
        }

        public DelegateCommand GreetCommand { get; }
        public DelegateCommand GoDetailCommand { get; }

        public HomeViewModel(IRegionManager regionManager)
        {
            _regionManager = regionManager;
            GreetCommand = new DelegateCommand(OnGreet);
            GoDetailCommand = new DelegateCommand(OnGoDetail);
        }

        private void OnGreet()
        {
            Message = $"你好,{InputName}!当前时间 {DateTime.Now:HH:mm:ss}";
        }

        private void OnGoDetail()
        {
            var parameters = new NavigationParameters { { "name", InputName } };
            _regionManager.RequestNavigate("ContentRegion", nameof(DetailView), parameters);
        }
    }
}

6.4 详情页:DetailView + DetailViewModel

xml 复制代码
<!-- DetailView.xaml -->
<UserControl x:Class="PrismDemo.Views.DetailView"
             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 Margin="24">
        <TextBlock Text="详情页" FontSize="26" FontWeight="Bold" Margin="0,0,0,20"/>
        <TextBlock FontSize="18">
            <Run Text="你好,"/>
            <Run Text="{Binding Name, Mode=OneWay}" FontWeight="Bold"/>
        </TextBlock>
        <TextBlock Text="这个值是通过导航参数从主页传过来的。" FontSize="14" Foreground="Gray" Margin="0,8,0,0"/>
        <Button Content="返回主页" Command="{Binding GoBackCommand}" Width="100" HorizontalAlignment="Left" Margin="0,24,0,0"/>
    </StackPanel>
</UserControl>
csharp 复制代码
// DetailView.xaml.cs
using System.Windows.Controls;

namespace PrismDemo.Views
{
    public partial class DetailView : UserControl
    {
        public DetailView()
        {
            InitializeComponent();
        }
    }
}
csharp 复制代码
// DetailViewModel.cs
using Prism.Commands;
using Prism.Mvvm;
using Prism.Regions;
using PrismDemo.Views;

namespace PrismDemo.ViewModels
{
    public class DetailViewModel : BindableBase, INavigationAware
    {
        private readonly IRegionManager _regionManager;

        private string _name = string.Empty;
        public string Name
        {
            get => _name;
            set => SetProperty(ref _name, value);
        }

        public DelegateCommand GoBackCommand { get; }

        public DetailViewModel(IRegionManager regionManager)
        {
            _regionManager = regionManager;
            GoBackCommand = new DelegateCommand(OnGoBack);
        }

        private void OnGoBack()
        {
            _regionManager.RequestNavigate("ContentRegion", nameof(HomeView));
        }

        // 导航到本页时,取出传递的参数
        public void OnNavigatedTo(NavigationContext navigationContext)
        {
            if (navigationContext.Parameters.ContainsKey("name"))
            {
                Name = navigationContext.Parameters.GetValue<string>("name");
            }
        }

        public bool IsNavigationTarget(NavigationContext navigationContext) => true;

        public void OnNavigatedFrom(NavigationContext navigationContext)
        {
        }
    }
}

七、运行效果

  1. 启动应用,ContentRegion 区域显示主页,输入框输入"张三"。
  2. 点"打招呼",下方绿色文字显示:你好,张三!当前时间 20:10:33
  3. 点"去详情页",区域切换到详情页,显示"你好,张三",说明导航参数传递成功。
  4. 点"返回主页",区域又切回主页。

整个过程中,页面切换、参数传递、命令绑定、属性通知,全部由 Prism 的约定和 DI 自动完成,代码后置文件几乎为空。

八、总结

  1. Prism 通过 PrismApplication 引导启动,用 DI 容器管理对象生命周期。
  2. Shell + Region 让页面可以动态组合,配合 Navigation 实现松耦合的页面切换。
  3. ViewModelLocator + BindableBase + DelegateCommand 构成了最核心的 MVVM 骨架,代码后置文件只需 InitializeComponent()
  4. 本文示例只覆盖了入门部分,Prism 的模块化、EventAggregator、DialogService 等更高级能力留待后续。

掌握本文内容后,你就已经能用 Prism 搭出结构清晰的 WPF 应用了。

相关推荐
xcLeigh1 小时前
Go入门:无类型常量与类型常量的区别
服务器·开发语言·golang
泡沫冰@1 小时前
GO 语言基础
开发语言·算法·golang
键盘会跳舞1 小时前
C++:std::tuple 源码级深度拆解——变参模板、SFINAE与模板元编程核心技巧
开发语言·c++·sfinae·变参模板
东华万里2 小时前
第40篇C++核心基础与工程实践:从底层逻辑到避坑指南
开发语言·c++·面试·大学生专区
wuyk5552 小时前
第 6 章 FOC 完整系统整合:从算法到真机跑起来
c语言·开发语言·stm32·单片机·嵌入式硬件
慧都小妮子2 小时前
C# 实现AI合同审查:从读取、风险标注到批量签发
ai·自然语言处理·c#·.net·办公自动化·ai合同审查·文档ai代理
luj_17682 小时前
元设计的诱惑与现实
c语言·开发语言·c++·经验分享·算法
小星星闪亮登场2 小时前
ST表--倍增思想
开发语言·数据结构·c++·算法·思维
东华万里2 小时前
第40篇 C++核心基础与工程实践:从底层逻辑到避坑指南
开发语言·c++·大学生专区