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

相关推荐
计算机毕设定制辅导-无忧学长20 分钟前
《基于微信小程序的居家医疗陪护预约系统设计与实现》
java·spring boot·微信小程序·uniapp·居家医疗陪护预约系统
.痞老板.24 分钟前
mysql初阶--增删改查操作
数据库·mysql
去伪存真1 小时前
AI Agent Harness 深度测评与技术选型实战
前端·agent
文丑颜不良啊2 小时前
LeetCode2221~2240题目解析
java·leetcode
秋饼2 小时前
Computer Use Agent Java 落地实战:感知-行动循环、执行器与安全围栏生产级实现
java·ai·技术分享·后端开发
文丑颜不良啊4 小时前
LeetCode2161~2180题目解析
java·leetcode
FRing034 小时前
vue2干货总结7(自定义指令、插槽、单页应用程序、路由的使用(VueRouter))
开发语言·前端·vue.js·vue2
文丑颜不良啊5 小时前
LeetCode2141~2160题目解析
java·leetcode
LabVIEW开发6 小时前
LabVIEW 连接板:28 个端子怎么排,优先级怎么定
java·算法·labview
lambo mercy6 小时前
AI智能租房助手
数据库