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

相关推荐
宸津-代码粉碎机4 小时前
OpenAI 连夜迎战 Grok Bot 和 Muse:AI 智能体从 “会聊天” 到 “能办事”,现在入场还来得及吗
java·大数据·人工智能·分布式·python
weixin_511255215 小时前
MySQL升级记录
数据库·mysql
噢,我明白了6 小时前
java中唯一键和幂等键的应用
java·后端
程序猿乐锅6 小时前
【黑马点评 | 第八篇】Redisson分布式锁
java·数据库·spring boot·redis·分布式·spring·缓存
福兮说6 小时前
前端下载大文件,点了按钮半天没反应:fetch + blob 的三个问题和三种替代写法
前端·javascript·性能优化·文件下载
GreenTea6 小时前
我把 Agent 的 while 循环拆掉了:一种你可能没想到的 Agent 架构
前端·后端·架构
Leo.yuan6 小时前
2026年本地化Data Agent优质厂商盘点:哪些产品更适合企业生产环境
大数据·数据库·人工智能
考虑考虑6 小时前
synchronized字符串常量
java·后端·java ee
长谷深风1116 小时前
Tool与Skill:AI能力设计的分水岭
java·人工智能·ai·大模型·aiagent
科技观察哨6 小时前
六足平台选型与纳米定位系统集成:HEB-640六自由度位移台在半导体光刻对准中的参数边界与国产替代评估
前端·人工智能