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報錯
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);
}
轉載請註明出處,歡迎交流。