- A+
所属分类:.NET技术
WPFUI报错
page does not have a parameterless constructor. If you are using Wpf.Ui.IPageService do not navigate initially and don't use Cache or Precache
问题原因
WPFUI中的NavigationView只支持导航页面的无参构造函数或含一个dataContext的有参构造函数。因为在View的构造函数中注入了一些服务,导致View创建失败,WPFUI报错。
问题处理
查看异常堆栈,找报错位置:
在 Wpf.Ui.Controls.NavigationViewActivator.CreateInstance(Type pageType, Object dataContext) 在 Wpf.Ui.ControlsNavigationViewActivator.cs 中: 第 37 行 在 Wpf.Ui.Controls.NavigationView.GetPageInstanceFromCache(Type targetPageType) 在 Wpf.Ui.ControlsNavigationView.cs 中: 第 1206 行 在 Wpf.Ui.Controls.NavigationCache.Remember(Type entryType, NavigationCacheMode cacheMode, Func`1 generate) 在 Wpf.Ui.ControlsNavigationCache.cs 中: 第 25 行 在 Wpf.Ui.Controls.NavigationView.GetNavigationItemInstance(INavigationViewItem viewItem) 在 Wpf.Ui.ControlsNavigationView.cs 中: 第 1185 行 在 Wpf.Ui.Controls.NavigationView.NavigateInternal(INavigationViewItem viewItem, Object dataContext, Boolean addToNavigationStack, Boolean isBackwardsNavigated) 在 Wpf.Ui.ControlsNavigationView.cs 中: 第 1131 行 在 Wpf.Ui.Controls.NavigationViewItem.OnClick() 在 Wpf.Ui.ControlsNavigationViewItem.cs 中: 第 314 行 ...
看源码,找解决方案:
private object GetNavigationItemInstance(INavigationViewItem viewItem) { if (viewItem.TargetPageType is null) { throw new InvalidOperationException( $"The {nameof(viewItem)}.{nameof(viewItem.TargetPageType)} property cannot be null." ); } if (_serviceProvider is not null) { return _serviceProvider.GetService(viewItem.TargetPageType) ?? throw new InvalidOperationException( $"{nameof(_serviceProvider)}.{nameof(_serviceProvider.GetService)} returned null for type {viewItem.TargetPageType}." ); } if (_pageService is not null) { return _pageService.GetPage(viewItem.TargetPageType) ?? throw new InvalidOperationException( $"{nameof(_pageService)}.{nameof(_pageService.GetPage)} returned null for type {viewItem.TargetPageType}." ); } return _cache.Remember( viewItem.TargetPageType, viewItem.NavigationCacheMode, ComputeCachedNavigationInstance ) ?? throw new InvalidOperationException( $"Unable to get or create instance of {viewItem.TargetPageType} from cache." ); object? ComputeCachedNavigationInstance() => GetPageInstanceFromCache(viewItem.TargetPageType); }
可以看到,如果提供了serviceProvider或者pageService,就可以通过容器获取View实例,不需要通过NavigationViewActivator.CreateInstance创建实例了。
提供ServiceProvider,例如使用Prism:
public class PrismServiceProvider : IServiceProvider { private readonly IContainerProvider _containerProvider; public PrismServiceProvider(IContainerProvider containerProvider) { _containerProvider = containerProvider; } public object GetService(Type serviceType) { return _containerProvider.Resolve(serviceType); } }
WPFUI中未提供默认的IPageService实现,但demo.mvvm中提供了基于IServiceProvider的实现,可以参考。本文基于Prism实现:
public class PageService : IPageService { private readonly IContainerProvider _containerProvider; public PageService(IContainerProvider containerProvider) { _containerProvider = containerProvider; } public T GetPage<T>() where T : class { if (!typeof(FrameworkElement).IsAssignableFrom(typeof(T))) throw new InvalidOperationException("The page should be a WPF control."); return (T)_containerProvider.Resolve(typeof(T)); } public FrameworkElement GetPage(Type pageType) { if (!typeof(FrameworkElement).IsAssignableFrom(pageType)) throw new InvalidOperationException("The page should be a WPF control."); return _containerProvider.Resolve(pageType) as FrameworkElement; } }
最后一步,将PrismServiceProvider、PageService注入容器,并通过INavigationService为NavigationView设置IServiceProvider和IPageService:
// App.xaml.cs protected override void RegisterTypes(IContainerRegistry containerRegistry) { var serviceProvider = new PrismServiceProvider(Container); containerRegistry.RegisterInstance<IServiceProvider>(serviceProvider); containerRegistry.RegisterSingleton<IPageService, PageService>(); containerRegistry.RegisterSingleton<INavigationService, NavigationService>(); } // MainWindow.xaml.cs public MainWindow(INavigationService navigationService, IPageService pageService, ISnackbarService snackbarService) { InitializeComponent(); navigationService.SetPageService(pageService); navigationService.SetNavigationControl(NavigationView); }