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),不同宿主控件对应不同的行为:
ContentControl→ 单视图,导航时替换内容。ItemsControl→ 多视图,可同时显示多个视图。TabControl→ 多视图,每个视图一个 Tab 页。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 还支持从外部加载模块:
- DirectoryModuleCatalog:扫描某个目录下的程序集,自动发现并加载模块(适合插件式架构)。
- ConfigurationModuleCatalog:从 App.config 里配置模块列表。
- 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),把菜单命令和内容导航联动起来,这部分在《导航深入》里展开。
五、总结
- Region 是视图的"挂载点",Prism 通过区域适配器把视图注入到
ContentControl/ItemsControl/TabControl等宿主中。 - 往区域放视图有 View Discovery、View Injection、Navigation 三种方式,Navigation 最完整、最推荐。
- 模块是
IModule的实现,用ConfigureModuleCatalog声明,用DirectoryModuleCatalog等实现插件式动态加载。 - 模块化让大型应用可以按功能拆分、独立开发、按需加载,是 Prism 区别于普通 MVVM 框架的核心价值。
下一篇《导航深入》讲 RequestNavigate 的参数、生命周期回调、离开确认和导航日志回退。