WPF中XAML实例化视图时Simple Injector Verify触发NullReferenceException
你遇到的问题是两个核心点共同导致的:Simple Injector的验证机制在应用上下文未就绪时实例化WPF窗口,以及XAML中的视图默认不通过容器实例化。让我一步步帮你理清并解决:
问题根源分析
Verify()触发的窗口实例化问题:
调用container.Verify()时,Simple Injector会尝试创建所有已注册类型的实例来验证依赖链完整性。但WPF的MainWindow实例化时会执行InitializeComponent(),这个方法依赖于WPF应用的运行时上下文(比如Application实例已启动)。而你的Bootstrap()方法在应用启动前执行,此时上下文不存在,因此抛出NullReferenceException。XAML视图未通过容器实例化:
你在MainWindow的XAML中直接声明<view:MyView />,WPF默认会用内置对象创建器实例化控件,只会调用无参构造函数,完全不与Simple Injector交互。这就是为什么MyView(MyViewModel)构造函数从未执行——WPF根本没走容器的实例化路径。
分步解决方案
1. 调整容器注册,避免验证时实例化UI控件
UI控件(窗口、用户控件)不需要在容器验证阶段被实例化,我们可以告诉Simple Injector跳过对这些类型的验证,或者用延迟加载方式注册:
private static Container Bootstrap() { var container = new Container(); // 注册视图模型(纯类,验证无问题) container.Register<MainWindowViewModel>(Lifestyle.Singleton); container.Register<MyViewModel>(Lifestyle.Singleton); // 注册UI控件,同时抑制验证 container.Register<MainWindow>(Lifestyle.Singleton); container.Register<MyView>(Lifestyle.Singleton); // 告诉容器不要在Verify时验证这些UI类型 container.Options.SuppressVerificationFor<MainWindow>(); container.Options.SuppressVerificationFor<MyView>(); container.Verify(); return container; }
或者用Func<T>延迟控件实例化,验证时仅注册工厂,不会创建控件实例:
container.Register<Func<MainWindow>>(() => () => container.GetInstance<MainWindow>()); container.Register<Func<MyView>>(() => () => container.GetInstance<MyView>());
2. 让WPF使用Simple Injector实例化XAML中的视图
要让XAML里的MyView通过容器创建,我们需要替换WPF的默认实例方式,最简单的方法是实现自定义MarkupExtension:
第一步:创建InjectExtension类
using System; using System.Windows.Markup; using SimpleInjector; namespace WpfPlayground { public class InjectExtension : MarkupExtension { // 假设App类持有容器实例 private readonly Container _container = ((App)Application.Current).Container; public Type Type { get; set; } public override object ProvideValue(IServiceProvider serviceProvider) { return _container.GetInstance(Type); } } }
第二步:修改App类,持有容器实例并在启动时创建窗口
using System.Windows; using SimpleInjector; namespace WpfPlayground { public partial class App : Application { public Container Container { get; private set; } protected override void OnStartup(StartupEventArgs e) { Container = Bootstrap(); // 从容器获取MainWindow并显示 var mainWindow = Container.GetInstance<MainWindow>(); mainWindow.Show(); base.OnStartup(e); } private static Container Bootstrap() { var container = new Container(); container.Register<MainWindowViewModel>(Lifestyle.Singleton); container.Register<MyViewModel>(Lifestyle.Singleton); container.Register<MainWindow>(Lifestyle.Singleton); container.Register<MyView>(Lifestyle.Singleton); container.Options.SuppressVerificationFor<MainWindow>(); container.Options.SuppressVerificationFor<MyView>(); container.Verify(); return container; } } }
第三步:修改MainWindow的XAML,使用InjectExtension加载MyView
<Window x:Class="WpfPlayground.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:WpfPlayground" xmlns:view="clr-namespace:WpfPlayground.View" mc:Ignorable="d" Title="MyApp" Height="450" Width="800"> <Grid> <!-- 用自定义扩展替换直接声明的MyView --> <local:Inject Type="{x:Type view:MyView}" /> </Grid> </Window>
3. 优化MainWindow的构造函数(可选)
既然用了依赖注入,最好把MainWindowViewModel也通过构造函数注入,而非手动设置空ViewModel:
public partial class MainWindow : Window { public MainWindow(MainWindowViewModel viewModel) { InitializeComponent(); DataContext = viewModel; } }
这样容器创建MainWindow时会自动注入对应ViewModel,符合依赖注入规范。
额外说明
Simple Injector的WPF示例通常会简化细节,比如跳过XAML视图的注入部分,因为这部分需要结合WPF的实例化机制。你之前的猜测没错——确实需要额外步骤让XAML中的视图走容器,而非自动完成。
内容的提问来源于stack exchange,提问作者Oliver Giesen

