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(视图注入,手动控制)

先 Add 再 Activate,可以精确控制哪个视图激活、传参、复用:

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);MainModule 和 SettingsModule 各自往内容区域注册视图。

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 的参数、生命周期回调、离开确认和导航日志回退。

相关推荐
奇牙coding8 分钟前
Codex CLI 升级后多文件编辑静默失效:config.yaml 枚举变更排查与修复指南
java·前端·javascript·ai
小羊没烦恼!14 分钟前
Ajax与JSON的一些总结
android·java·前端·数据库·mysql·算法·okhttp
程序员老赵17 分钟前
Docker 部署 Filestash:轻松搭建浏览器文件管理平台
前端·docker·开源
注塑机数据采集联网18 分钟前
如何解决注塑机数据采集联网对接MES系统数据WiFi传输数据不稳问题
大数据·数据库·物联网·数据挖掘·数据分析
夏天要喝冰可乐18 分钟前
给 23 万字修仙小说造个阅读站:浏览器内打包 EPUB 分页阅读 + 三主题实战
前端·产品·全栈
Lhappy嘻嘻21 分钟前
读懂 JVM:内存模型、类加载机制、分代回收
java·开发语言·jvm
黄俊懿21 分钟前
【架构师从入门到进阶】第六章:服务集群优化——第八节:线程数公式
java·服务器·数据库·分布式·性能优化·架构·系统架构
茶底世界之下23 分钟前
林间步道要不要加暗角?用 Awaking 把强度收在20%
前端·ios·图片资源
执子念的飞鱼23 分钟前
纯前端预览 275 种文件格式是怎么做到的
前端·vue.js·react.js
Rain的Java大神之路28 分钟前
别再死磕 MySQL 了:向量数据库与 RAG 生产落地实录(Java 工程师专场)
java·后端·面试