WPFUI报错 - page does not have a parameterless constructor
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.Controls\NavigationViewActivator.cs 中: 第 37 行
在 Wpf.Ui.Controls.NavigationView.GetPageInstanceFromCache(Type targetPageType) 在 Wpf.Ui.Controls\NavigationView.cs 中: 第 1206 行
在 Wpf.Ui.Controls.NavigationCache.Remember(Type entryType, NavigationCacheMode cacheMode, Func`1 generate) 在 Wpf.Ui.Controls\NavigationCache.cs 中: 第 25 行
在 Wpf.Ui.Controls.NavigationView.GetNavigationItemInstance(INavigationViewItem viewItem) 在 Wpf.Ui.Controls\NavigationView.cs 中: 第 1185 行
在 Wpf.Ui.Controls.NavigationView.NavigateInternal(INavigationViewItem viewItem, Object dataContext, Boolean addToNavigationStack, Boolean isBackwardsNavigated) 在 Wpf.Ui.Controls\NavigationView.cs 中: 第 1131 行
在 Wpf.Ui.Controls.NavigationViewItem.OnClick() 在 Wpf.Ui.Controls\NavigationViewItem.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);
}
转载请注明出处,欢迎交流。