WinUI NavigationView:Frame显示页面遇空引用错误及数据加载需求
问题分析与解决方案
核心问题定位
你遇到的"对象引用未设置为对象实例"错误,主要来自两个关键原因:
- 页面构造函数依赖无法解析:
ExploreCountriesPage只有带ExploreCountriesViewModel参数的构造函数,但WinUI的Frame.Navigate默认会尝试调用无参构造函数创建页面,导致实例创建失败,触发空引用异常。 - 异步初始化时机不当:在
ExploreCountriesViewModel的构造函数中直接调用异步方法InitializeDataAsync(),无法等待异步操作完成,不仅会导致数据加载时机不可控,还可能引发未捕获的异常。
分步解决方案
1. 修复页面导航的依赖注入问题
方式一:通过DI容器直接解析页面实例
首先在App.cs中注册页面类型:
private IServiceProvider RegisterServices() { var serviceCollection = new ServiceCollection(); serviceCollection.AddSingleton<IRadioBrowserClient>(new RadioBrowserClient(apiUrl: "de1.api.radio-browser.info")); serviceCollection.AddSingleton<ExploreCountriesViewModel>(); serviceCollection.AddTransient<ExploreCountriesPage>(); // 注册页面 return serviceCollection.BuildServiceProvider(); }
然后修改MainWindow.cs,延迟到窗口加载完成后,从DI容器获取页面实例再导航:
public sealed partial class MainWindow : Window { public MainWindow() { this.InitializeComponent(); this.Loaded += MainWindow_Loaded; } private void MainWindow_Loaded(object sender, RoutedEventArgs e) { var container = ((App)Application.Current).Container; var page = container.GetService<ExploreCountriesPage>(); ContentFrame.Navigate(page); } }
这种方式下,DI容器会自动将ExploreCountriesViewModel注入到页面的构造函数中,无需手动传递参数。
方式二:通过导航参数传递ViewModel
如果不想注册页面到DI容器,可以保留原页面构造函数,修改MainWindow.cs传递ViewModel:
private void MainWindow_Loaded(object sender, RoutedEventArgs e) { var container = ((App)Application.Current).Container; var viewModel = container.GetService<ExploreCountriesViewModel>(); ContentFrame.Navigate(typeof(ExploreCountriesPage), viewModel); }
同时修改ExploreCountriesPage.cs,在导航时接收参数:
public sealed partial class ExploreCountriesPage : Page { public ExploreCountriesViewModel CountriesViewModel { get; set; } // 保留带参数的构造函数,同时支持导航参数传递 public ExploreCountriesPage() { this.InitializeComponent(); } public ExploreCountriesPage(ExploreCountriesViewModel viewModel) { this.InitializeComponent(); CountriesViewModel = viewModel; DataContext = CountriesViewModel; } protected override void OnNavigatedTo(NavigationEventArgs e) { base.OnNavigatedTo(e); if (e.Parameter is ExploreCountriesViewModel viewModel && CountriesViewModel == null) { CountriesViewModel = viewModel; DataContext = CountriesViewModel; } _ = CountriesViewModel.InitializeDataAsync(); } }
2. 调整ViewModel异步初始化逻辑
移除ExploreCountriesViewModel构造函数中的异步方法调用,改为在页面导航完成后触发:
public ExploreCountriesViewModel(IRadioBrowserClient radioBrowserClient) { this.radioBrowserClient = radioBrowserClient; // 移除这里的 InitializeDataAsync(); 调用 }
确保数据加载操作在页面绑定完成后执行,避免数据未就绪时的绑定异常。
3. 优化NavigationView菜单导航逻辑
如果需要通过NavigationView的菜单切换页面,同样使用DI容器解析实例:
private void NavigationView_SelectionChanged(NavigationView sender, NavigationViewSelectionChangedEventArgs args) { if (args.SelectedItem is NavigationViewItem item) { var tag = item.Tag.ToString(); Page page = null; var container = ((App)Application.Current).Container; switch(tag) { case "Explore": page = container.GetService<ExploreCountriesPage>(); break; // 其他页面的处理逻辑 } if(page != null && ContentFrame.Content != page) { ContentFrame.Navigate(page); } } }
最终效果
完成上述修改后,MainWindow的ContentFrame会成功加载ExploreCountriesPage,页面显示时会自动触发数据加载,同时解决了空引用异常问题。
内容的提问来源于stack exchange,提问作者Sisimośki
相关产品推荐
相关产品推荐

