WPFUI报错 – page does not have a parameterless constructor

  • WPFUI报错 – page does not have a parameterless constructor已关闭评论
  • 7 次浏览
  • A+
所属分类:.NET技术
摘要

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报错

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); }