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

相关推荐
刃神太酷啦41 分钟前
Redis 进阶核心:持久化 (RDB/AOF)、事务与主从复制全解析----《Hello Redis!》(5)
linux·c语言·数据库·c++·redis·缓存·bootstrap
一技安身1 小时前
【信创】Docker‑Compose V2 两种离线部署(独立模式、插件模式)简易教程
java·docker·eureka
丁丁点灯o1 小时前
Oracle中使用外键的场景及不适用外键的情况
数据库·oracle
tsumikistep1 小时前
【前端】Vue + Axios 跨域问题实战:7070 代理转发 8080 + 401 报错分析(黑马外卖)
前端·javascript·vue.js
七夜zippoe1 小时前
为什么 2026 年每个 Java 团队都该懂 AI Agent
java·开发语言·人工智能
天天进步20151 小时前
Pixelle-Video 源码解析 #18:声音克隆功能:参考音频如何影响解说效果?
数据库·音视频
时凌云.1 小时前
【2026最新】JDK 下载安装与环境配置全教程(Windows/Mac/Linux 三平台,零基础友好)
java·linux·macos
lichenyang4531 小时前
鸿蒙首页从等高商品 Grid 到双列瀑布流:同一张图也能做出小红书式浏览节奏
前端
里欧跑得慢1 小时前
Flutter主题与样式详解
前端·css·flutter·web
计算机魔术师1 小时前
TPU推理性价比翻50%,NVIDIA的护城河要见底了?
前端