C# WPF Prism 进阶(二):区域(Region)与模块化(Module)

C# WPF Prism 进阶(二):区域(Region)与模块化(Module)

前言

上一篇《入门篇》我们搭出了一个能跑起来的 Prism 应用,用到了 Shell + 一个 ContentControl 区域 + 导航。但那只是一个"单页面切换"的雏形。真正的 Prism 项目,核心诉求是把一个大应用拆成多个可独立开发、独立加载的模块(Module),每个模块再把自己的视图注入到 Shell 的"区域(Region)"里。

本文深入讲两件事:Region 的三种用法模块化(Module),并给出可运行的示例。

版本说明:本文基于 Prism 9.x 。Region 相关类型在命名空间 Prism.Navigation.Regions,模块相关在 Prism.Modularity

一、Region 是什么

Region 是 Shell(或任意视图)上的一个"命名占位区域",用附加属性 RegionManager.RegionName 标记。视图不是直接被 new 出来塞进去,而是"注册"或"导航"到某个区域中。

xml 复制代码
<ContentControl prism:RegionManager.RegionName="ContentRegion"/>

Prism 内置了多种区域适配器(Region Adapter),不同宿主控件对应不同的行为:

  1. ContentControl → 单视图,导航时替换内容。
  2. ItemsControl → 多视图,可同时显示多个视图。
  3. TabControl → 多视图,每个视图一个 Tab 页。
  4. Selector(如 ListBox)→ 多视图,选中项对应视图。

二、往 Region 里放视图的三种方式

方式一:View Discovery(视图发现,一次性注册)

RegisterViewWithRegion 把视图和区域名绑定,区域创建时自动加载:

csharp 复制代码
_regionManager.RegisterViewWithRegion<HomeView>("ContentRegion");

特点:简单,适合"固定内容"的区域;但视图实例由 Prism 管理,难以精细控制生命周期和传参。

方式二:View Injection(视图注入,手动控制)

AddActivate,可以精确控制哪个视图激活、传参、复用:

csharp 复制代码
var view = _container.Resolve<HomeView>();
region.Add(view);
region.Activate(view);

特点:灵活,但代码量稍多。

方式三:Navigation(导航,推荐)

RegisterForNavigation<T>() 注册,再 RequestNavigate 切换:

csharp 复制代码
_regionManager.RequestNavigate("ContentRegion", nameof(DetailView));

特点:支持参数传递、回退、生命周期回调(IRegionAware),是 Prism 最完整的用法,上一篇已经用过。

三、模块化(Module)

3.1 什么是模块

模块是一个实现了 IModule 接口的类,把"一类功能"(视图、服务、业务逻辑)打包成一个可独立加载的单元:

csharp 复制代码
using Prism.Ioc;
using Prism.Modularity;
using Prism.Navigation.Regions;
using PrismDemo.Modules.Main.Views;

namespace PrismDemo.Modules.Main
{
    public class MainModule : IModule
    {
        private readonly IRegionManager _regionManager;

        // 模块可以注入容器里的服务(如 IRegionManager)
        public MainModule(IRegionManager regionManager)
        {
            _regionManager = regionManager;
        }

        // 模块加载完成后调用,适合做视图注册
        public void OnInitialized(IContainerProvider containerProvider)
        {
            _regionManager.RegisterViewWithRegion<HomeView>("ContentRegion");
        }

        // 注册模块内部的服务
        public void RegisterTypes(IContainerRegistry containerRegistry)
        {
            containerRegistry.RegisterSingleton<IMainService, MainService>();
        }
    }
}

3.2 声明模块目录

App.xaml.cs 里通过 ConfigureModuleCatalog 告诉 Prism 有哪些模块:

csharp 复制代码
using Prism.Modularity;
using PrismDemo.Modules.Main;
using PrismDemo.Modules.Settings;

public partial class App : PrismApplication
{
    protected override void ConfigureModuleCatalog(IModuleCatalog moduleCatalog)
    {
        moduleCatalog.AddModule<MainModule>();
        moduleCatalog.AddModule<SettingsModule>();
    }
}

AddModule<T>()Prism.Modularity.IModuleCatalogExtensions 提供的扩展方法。

3.3 其他模块目录加载方式

除了代码里显式 AddModule,Prism 还支持从外部加载模块:

  1. DirectoryModuleCatalog:扫描某个目录下的程序集,自动发现并加载模块(适合插件式架构)。
  2. ConfigurationModuleCatalog:从 App.config 里配置模块列表。
  3. XamlModuleCatalog:从 XAML 文件定义模块。
csharp 复制代码
protected override IModuleCatalog CreateModuleCatalog()
{
    return new DirectoryModuleCatalog { ModulePath = @".\Modules" };
}

四、完整示例:两个模块

下面构建一个带两个模块的应用:Shell 左侧是菜单区域(ItemsControl),右侧是内容区域(ContentControl);MainModuleSettingsModule 各自往内容区域注册视图。

4.1 Shell

xml 复制代码
<!-- MainWindow.xaml -->
<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="模块化示例" Height="400" Width="700">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="160"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>

        <!-- 左侧菜单区域:多视图列表 -->
        <ListBox Grid.Column="0" prism:RegionManager.RegionName="MenuRegion"/>

        <!-- 右侧内容区域:单视图 -->
        <ContentControl Grid.Column="1" prism:RegionManager.RegionName="ContentRegion"/>
    </Grid>
</Window>

4.2 MainModule

csharp 复制代码
using Prism.Ioc;
using Prism.Modularity;
using Prism.Navigation.Regions;
using PrismDemo.Modules.Main.Views;

namespace PrismDemo.Modules.Main
{
    public class MainModule : IModule
    {
        private readonly IRegionManager _regionManager;

        public MainModule(IRegionManager regionManager)
        {
            _regionManager = regionManager;
        }

        public void OnInitialized(IContainerProvider containerProvider)
        {
            _regionManager.RegisterViewWithRegion<HomeView>("ContentRegion");
            _regionManager.RegisterViewWithRegion<HomeMenuView>("MenuRegion");
        }

        public void RegisterTypes(IContainerRegistry containerRegistry)
        {
        }
    }
}

4.3 SettingsModule

csharp 复制代码
using Prism.Ioc;
using Prism.Modularity;
using Prism.Navigation.Regions;
using PrismDemo.Modules.Settings.Views;

namespace PrismDemo.Modules.Settings
{
    public class SettingsModule : IModule
    {
        private readonly IRegionManager _regionManager;

        public SettingsModule(IRegionManager regionManager)
        {
            _regionManager = regionManager;
        }

        public void OnInitialized(IContainerProvider containerProvider)
        {
            _regionManager.RegisterViewWithRegion<SettingsView>("ContentRegion");
            _regionManager.RegisterViewWithRegion<SettingsMenuView>("MenuRegion");
        }

        public void RegisterTypes(IContainerRegistry containerRegistry)
        {
        }
    }
}

提示:两个模块都往 ContentRegion 注册视图时,会各自创建视图实例。要实现"菜单点击切换内容",更推荐用 Navigation(RequestNavigate),把菜单命令和内容导航联动起来,这部分在《导航深入》里展开。

五、总结

  1. Region 是视图的"挂载点",Prism 通过区域适配器把视图注入到 ContentControl/ItemsControl/TabControl 等宿主中。
  2. 往区域放视图有 View Discovery、View Injection、Navigation 三种方式,Navigation 最完整、最推荐
  3. 模块是 IModule 的实现,用 ConfigureModuleCatalog 声明,用 DirectoryModuleCatalog 等实现插件式动态加载。
  4. 模块化让大型应用可以按功能拆分、独立开发、按需加载,是 Prism 区别于普通 MVVM 框架的核心价值。

下一篇《导航深入》讲 RequestNavigate 的参数、生命周期回调、离开确认和导航日志回退。

相关推荐
AI人工智能+电脑小能手1 小时前
大白话说Java设计模式-23-桥接模式(源码剖析篇)
java·设计模式·jdbc·桥接模式·源码分析·awt·java logging
long3161 小时前
枚举(Enums)
java·开发语言·数据库
TJHHH.1 小时前
SQL注入学习总结
数据库·笔记·sql·学习·注入
xyphf_和派孔明1 小时前
Vite 与 Webpack 对比及常见面试题
前端·webpack·vite
明月_清风1 小时前
Pi Agent 深度解析:开源极简终端 AI 编码代理的终极指南
前端·后端·ai编程
墨雨晨曦881 小时前
2026/08/15 spring AI学习总结
java·tomcat
fatcoder2 小时前
玩转Nginx 03 — location 匹配规则:让不同的路径各回各家
前端·后端·nginx
HjhIron2 小时前
前端面试高频考点 —— TS 高级类型 & CSS 三列布局
前端
wno7042 小时前
Spring Boot JdbcTemplate配置Druid多数据源
java·spring boot·后端