英文:
Odd dependency injection behavior with Views & ViewModels using Template Studio for WinUI
问题
Using Template Studio for WinUI,我创建了一个包含两个页面(Main和Foo)的应用程序。如果将视图模型传递到页面的构造函数中,应用程序会生成异常,但如果使用 App.GetService<xViewModel>()
来获取视图模型,其中x是页面的视图模型名称,一切都正常工作。将相同的视图模型传递到其他非页面类的构造函数中也可以正常工作。
有人能解释为什么将视图模型传递到页面的构造函数会失败吗?
以下是相关的代码片段:
生成的 Foo.xaml.cs 文件(运行良好):
using Ioc.ViewModels;
using Microsoft.UI.Xaml.Controls;
namespace Ioc.Views;
public sealed partial class FooPage : Page
{
public FooViewModel ViewModel
{
get;
}
public FooPage()
{
ViewModel = App.GetService<FooViewModel>();
InitializeComponent();
}
}
Foo.xaml.cs - 将视图模型传递到构造函数中(抛出异常):
public FooPage(FooViewModel viewModel)
{
ViewModel = viewModel;
InitializeComponent();
}
当尝试导航到 Foo 页面并将视图模型传递到构造函数时,会出现以下异常:
System.NullReferenceException: 'Object reference not set to an instance of an object.'
at Ioc.Ioc_XamlTypeInfo.XamlUserType.ActivateInstance() in C:\PathToProject\Ioc\obj\x64\Debug\net7.0-windows10.0.19041.0\win10-x64\XamlTypeInfo.g.cs:line 2602
App.xaml.cs 构造函数中注册 FooViewModel 服务的部分:
```csharp
public App()
{
InitializeComponent();
Host = Microsoft.Extensions.Hosting.Host
.CreateDefaultBuilder()
.UseContentRoot(AppContext.BaseDirectory)
.ConfigureServices((context, services) =>
{
// 默认激活处理程序
services.AddTransient<ActivationHandler<LaunchActivatedEventArgs>, DefaultActivationHandler>();
// 其他激活处理程序
// 服务
services.AddTransient<INavigationViewService, NavigationViewService>();
services.AddSingleton<IActivationService, ActivationService>();
services.AddSingleton<IPageService, PageService>();
services.AddSingleton<INavigationService, NavigationService>();
// 核心服务
services.AddSingleton<IFileService, FileService>();
// 视图和视图模型
services.AddTransient<FooViewModel>();
services.AddTransient<FooPage>();
services.AddTransient<MainViewModel>();
services.AddTransient<MainPage>();
services.AddTransient<ShellPage>();
services.AddTransient<ShellViewModel>();
// 配置
})
.Build();
UnhandledException += App_UnhandledException;
}
编辑
使用帧并使用以下导航方法进行导航:
private Frame? _frame; // 类型为 Microsoft.UI.Xaml.Controls.Frame
public bool NavigateTo(string pageKey, object? parameter = null, bool clearNavigation = false)
{
var pageType = _pageService.GetPageType(pageKey);
if (_frame != null && (_frame.Content?.GetType() != pageType || (parameter != null && !parameter.Equals(_lastParameterUsed))))
{
_frame.Tag = clearNavigation;
var vmBeforeNavigation = _frame.GetPageViewModel();
var navigated = _frame.Navigate(pageType, parameter);
if (navigated)
{
_logger.LogDebug("Navigated to {PageType}", pageType.Name);
_lastParameterUsed = parameter;
if (vmBeforeNavigation is INavigationAware navigationAware)
{
navigationAware.OnNavigatedFrom();
}
}
return navigated;
}
return false;
}
代码在以下行引发异常:
var navigated = _frame.Navigate(pageType, parameter);
希望这些信息对你有所帮助。
英文:
Using Template Studio for WinUI, I created an app with 2 pages, Main and Foo. If the view model is passed into the page's constructor the application generates an exception, but if the view model is grabbed using App.GetService<xViewModel>();
where x is the name of the view model for the page, everything works great. Passing the same view model into other non-page class constructors works great as well.
Can someone explain why passing the view model into the page's constructor fails?
Code
Generated Foo.xaml.cs file (works great):
using Ioc.ViewModels;
using Microsoft.UI.Xaml.Controls;
namespace Ioc.Views;
public sealed partial class FooPage : Page
{
public FooViewModel ViewModel
{
get;
}
public FooPage()
{
ViewModel = App.GetService<FooViewModel>();
InitializeComponent();
}
}
Foo.xaml.cs - passing view model into the constructor (throws exception):
public FooPage(FooViewModel viewModel)
{
ViewModel = viewModel;
InitializeComponent();
}
the following exception occurs when trying to navigate to the Foo page when view model is passed into the constructor:
>System.NullReferenceException: 'Object reference not set to an instance of an object.'
>
>at Ioc.Ioc_XamlTypeInfo.XamlUserType.ActivateInstance() in C:\PathToProject\Ioc\obj\x64\Debug\net7.0-windows10.0.19041.0\win10-x64\XamlTypeInfo.g.cs:line 2602
App.xaml.cs constructor where the FooViewModel service is registered:
public App()
{
InitializeComponent();
Host = Microsoft.Extensions.Hosting.Host.
CreateDefaultBuilder().
UseContentRoot(AppContext.BaseDirectory).
ConfigureServices((context, services) =>
{
// Default Activation Handler
services.AddTransient<ActivationHandler<LaunchActivatedEventArgs>, DefaultActivationHandler>();
// Other Activation Handlers
// Services
services.AddTransient<INavigationViewService, NavigationViewService>();
services.AddSingleton<IActivationService, ActivationService>();
services.AddSingleton<IPageService, PageService>();
services.AddSingleton<INavigationService, NavigationService>();
// Core Services
services.AddSingleton<IFileService, FileService>();
// Views and ViewModels
services.AddTransient<FooViewModel>();
services.AddTransient<FooPage>();
services.AddTransient<MainViewModel>();
services.AddTransient<MainPage>();
services.AddTransient<ShellPage>();
services.AddTransient<ShellViewModel>();
// Configuration
}).
Build();
UnhandledException += App_UnhandledException;
}
Edit
Navigation using a frame and this Navigation method:
private Frame? _frame; // Type Microsoft.UI.Xaml.Controls.Frame
public bool NavigateTo(string pageKey, object? parameter = null, bool clearNavigation = false)
{
var pageType = _pageService.GetPageType(pageKey);
if (_frame != null && (_frame.Content?.GetType() != pageType || (parameter != null && !parameter.Equals(_lastParameterUsed))))
{
_frame.Tag = clearNavigation;
var vmBeforeNavigation = _frame.GetPageViewModel();
var navigated = _frame.Navigate(pageType, parameter);
if (navigated)
{
_logger.LogDebug("Navigated to {PageType}", pageType.Name);
_lastParameterUsed = parameter;
if (vmBeforeNavigation is INavigationAware navigationAware)
{
navigationAware.OnNavigatedFrom();
}
}
return navigated;
}
return false;
}
The code throws the exception on this line:
var navigated = _frame.Navigate(pageType, parameter);
答案1
得分: 1
By navigate I guess you mean navigation of a Frame
control but the navigation feature of Frame
s doesn't resolve your type like App.GetService<T>()
does.
What you can do is something like this:
In App.xaml.cs
public static bool TryGetService(Type serviceType, out object? service)
{
service = (App.Current as App)?.Host?.Services.GetService(serviceType);
return service is not null;
}
and in the navigation event handler:
private void NavigationView_SelectionChanged(NavigationView sender, NavigationViewSelectionChangedEventArgs args)
{
if (args.SelectedItem is NavigationViewItem navigationViewItem &&
navigationViewItem.Tag is string pageName &&
Type.GetType(pageName) &&
App.TryGetService(pageType, out object? page) is true)
{
this.ContentFrame.Content = page;
}
}
英文:
By navigate I guess you mean navigation of a Frame
control but the navigation feature of Frame
s doesn't resolve your type like App.GetService<T>()
does.
What you can do is something like this:
In App.xaml.cs
public static bool TryGetService(Type serviceType, out object? service)
{
service = (App.Current as App)?.Host?.Services.GetService(serviceType);
return service is not null;
}
and in the navigation event handler:
private void NavigationView_SelectionChanged(NavigationView sender, NavigationViewSelectionChangedEventArgs args)
{
if (args.SelectedItem is NavigationViewItem navigationViewItem &&
navigationViewItem.Tag is string pageName &&
Type.GetType(pageName) &&
App.TryGetService(pageType, out object? page) is true)
{
this.ContentFrame.Content = page;
}
}
``
</details>
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论