8.4 Region 与导航生命周期
学习目标
你将把任务页和设置页放入 MainRegion,判断一次导航是否成功,并决定页面何时复用、何时允许离开。前提:已完成 Prism 启动,使用 Prism.DryIoc 9.0.537;页面注册由 TasksModule 负责。
Region 与后端路由的对照
ASP.NET Core 路由为请求选择端点;Region 是窗口中承载视图的命名位置。MainRegion 表示“放在哪里”,Tasks 表示“显示谁”,它们是两种名称。普通 ContentControl 只知道内容,Prism 的 Region Adapter 为它增加视图集合、激活状态和导航服务;不同宿主控件的可同时激活视图数量不同。
以下片段放在已有 Shell.xaml 内,根元素已声明 xmlns:prism="http://prismlibrary.com/"。它不是第二个窗口,也不要同时给这个内容区绑定另一套 Content 页面管理逻辑。
<ContentControl prism:RegionManager.RegionName="MainRegion" />
视图注册不等于 Region 已创建。配套项目等待 Shell.Loaded 后再首次导航,因为宿主需要进入可视树并完成 Region 注册;在 Shell 构造函数或模块初始化时导航,可能找不到目标 Region。
发起导航并读取结果
下列是 ShellViewModel 的方法片段。前提:构造函数已注入并保存 _regionManager,NavigationStatus 是通知属性,Tasks、Settings 已通过 RegisterForNavigation 注册。
// 文件顶部:using Prism.Navigation;
// using Prism.Navigation.Regions;
private void Navigate(string target)
{
var parameters = new NavigationParameters
{
{ "source", "sidebar" }
};
_regionManager.RequestNavigate("MainRegion", target, result =>
{
NavigationStatus = result.Success
? $"已打开 {target}"
: result.Exception is not null
? $"导航失败:{result.Exception.Message}"
: "导航未继续(取消或拒绝)";
}, parameters);
}
参数可以携带对象,并不要求拼成查询字符串。页面用 navigationContext.Parameters.GetValue<string>("source") 读取约定值;实际业务应核对必需参数是否存在及类型。传任务标识并由应用服务查询,比把整棵可变对象图交给新页面更容易管理一致性。
本节的命名空间是 Prism.Navigation.Regions,参数在 Prism.Navigation。Prism 9 的导航结果读取 Success、Exception、Cancelled,不要套用旧教程的 Result、Error;下一节的对话框结果 Result 则是另一种 API。
9.0.537 的 WPF Region 细节: 离开确认回调返回 false 时,结果是 Success=false、Exception=null,Cancelled 仍可能为 false。因此本例先判断成功,再判断异常,最后把无异常的未成功结果显示为未继续;只依赖 Cancelled 会把正常拒绝误报成失败。
进入、离开与复用
Prism 会检查视图及其 DataContext 的导航接口。WPF 的 INavigationAware 兼容接口继承 IRegionAware:IsNavigationTarget 决定已有实例是否能处理本次请求,OnNavigatedTo 接收进入参数,OnNavigatedFrom 处理离开通知。它们不是每次都重新执行的构造函数,也不对应服务端的请求作用域。
下面是添加到 SettingsViewModel 的接口实现片段,类需实现 INavigationAware、IConfirmNavigationRequest、IRegionMemberLifetime,并已有 IsDirty 属性和保存命令。此教学策略要求先保存才能离开;配套项目还通过确认对话框询问用户。
// 文件顶部:using System; using Prism.Navigation.Regions;
public bool KeepAlive => true;
public bool IsNavigationTarget(NavigationContext navigationContext)
=> true; // 单个设置页总是复用;多文档页面应比较标识。
public void OnNavigatedTo(NavigationContext navigationContext)
{
// 可读取参数、刷新提示;不要无条件覆盖未保存输入。
}
public void OnNavigatedFrom(NavigationContext navigationContext)
{
// 根据业务策略暂停工作;离开不等同于销毁。
}
public void ConfirmNavigationRequest(
NavigationContext navigationContext,
Action<bool> continuationCallback)
{
continuationCallback(!IsDirty);
}
确认接口使用回调,允许先弹窗再作决定。每个请求最终必须调用回调一次:true 继续,false 取消。不要忘记取消和窗口关闭分支,也不要在同步回调后又从弹窗回调执行一次。
KeepAlive=true 让停用视图留在 Region 中,IsNavigationTarget=true 才允许它在后续请求中复用;两者解决不同问题。KeepAlive=false 使停用视图被移除,不代表 Prism 自动调用所有对象的 Dispose。取消任务、退订和资源释放仍需明确安排。
历史与常见错误
Region 的 NavigationService.Journal 提供 CanGoBack、GoBack 和前进功能。历史记录的是导航目标与参数,不能当成编辑内容快照;返回也可能触发离开确认。不要把 IsNavigationTarget=false 当成“禁止离开”,它只拒绝实例复用,离开许可应交给确认接口。
在 OnNavigatedTo 中直接写 async void 会让导航完成与数据加载完成脱节。需要加载时,通过受控异步命令或加载服务维护忙碌、错误和取消状态;导航回调成功只表示页面切换成功。
练习与验收
修改设置名称后切到任务页,取消离开应继续显示设置页且输入保留;保存后切换应成功。给设置页构造函数加断点,往返三次应复用同一实例。再临时改为 KeepAlive=false,保存后往返应创建新实例。请求一个未注册的导航名时,状态必须显示失败信息,而非“已打开”。
官方资料
官方文档部分示例保留了旧版命名;本节的 API 以 9.0.537 为准。下一节:Prism 9 对话框。