WinUI3中SelectorBar切换时复用子页面的DI实现方案
解决方案:WinUI3中实现子页面与父页面同生命周期的复用
方案1:手动缓存子页面实例(最简单直接)
无需修改现有DI注册,直接在OverviewPage内部维护子页面实例,切换视图时复用已创建对象:
- 在
OverviewPage中声明私有字段用于缓存实例:
private Page1? _page1; private Page2? _page2;
- 在
SelectorBar的SelectionChanged事件中,判断实例是否已创建,未创建则通过DI获取,否则直接复用:
private void SelectorBar_SelectionChanged(SelectorBar sender, SelectorBarSelectionChangedEventArgs args) { var selectedItem = args.SelectedItem as SelectorBarItem; if (selectedItem?.Tag.ToString() == "Page1") { _page1 ??= App.Current.Services.GetService<Page1>(); ContentFrame.Content = _page1; } else if (selectedItem?.Tag.ToString() == "Page2") { _page2 ??= App.Current.Services.GetService<Page2>(); ContentFrame.Content = _page2; } }
注:直接赋值
ContentFrame.Content而非调用Navigate,避免Frame导航栈创建新实例;若需保留导航栈逻辑,可自定义判断逻辑跳过实例重建。
- 在
OverviewPage销毁时显式清理缓存(可选,GC会自动回收,显式清理更严谨):
protected override void OnUnloaded(RoutedEventArgs e) { base.OnUnloaded(e); _page1 = null; _page2 = null; }
方案2:使用Scoped服务配合专属服务范围(符合DI设计)
将子页面注册为Scoped服务,为OverviewPage创建专属服务范围,子页面从该范围获取,实现与父页面同生命周期:
- 在
App.xaml.cs中修改DI注册为Scoped:
builder.Services.AddScoped<Page1>(); builder.Services.AddScoped<Page2>(); builder.Services.AddTransient<OverviewPage>();
- 在
OverviewPage中创建并持有专属服务范围,从该范围获取子页面实例:
private readonly IServiceScope _scope; private Page1? _page1; private Page2? _page2; public OverviewPage(IServiceProvider serviceProvider) { InitializeComponent(); // 创建父页面专属的服务范围 _scope = serviceProvider.CreateScope(); } private void SelectorBar_SelectionChanged(SelectorBar sender, SelectorBarSelectionChangedEventArgs args) { var selectedItem = args.SelectedItem as SelectorBarItem; if (selectedItem?.Tag.ToString() == "Page1") { _page1 ??= _scope.ServiceProvider.GetRequiredService<Page1>(); ContentFrame.Content = _page1; } else if (selectedItem?.Tag.ToString() == "Page2") { _page2 ??= _scope.ServiceProvider.GetRequiredService<Page2>(); ContentFrame.Content = _page2; } }
- 在
OverviewPage销毁时释放服务范围,自动销毁Scoped注册的子页面:
protected override void OnUnloaded(RoutedEventArgs e) { base.OnUnloaded(e); _scope.Dispose(); }
注意事项
- 方案1适合简单场景,代码量少,无需改动DI结构;
- 方案2更适合子页面依赖其他Scoped服务的场景,能保证整个依赖链的生命周期与父页面一致;
- 禁止使用
AddSingleton注册子页面,会导致实例在App生命周期内永久存在,不符合销毁需求。
内容的提问来源于stack exchange,提问作者xerxos
相关产品推荐
相关产品推荐

