本篇定位 CommunityToolkit.Mvvm 不内置 DI 容器,但可以轻松集成 Microsoft.Extensions.DependencyInjection。这篇用示例演示完整集成方案。
1. 安装依赖 1 2 <PackageReference Include ="Microsoft.Extensions.DependencyInjection" Version ="8.0.0" /> <PackageReference Include ="CommunityToolkit.Mvvm" Version ="8.2.0" />
2. 基础配置 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 public partial class App : Application { public static IServiceProvider Services { get ; private set ; } = null !; protected override void OnStartup (StartupEventArgs e ) { var services = new ServiceCollection(); services.AddSingleton<INoteService, NoteService>(); services.AddSingleton<IDialogService, DialogService>(); services.AddSingleton<IMessenger>(WeakReferenceMessenger.Default); services.AddTransient<MainViewModel>(); services.AddTransient<NoteListViewModel>(); services.AddTransient<NoteDetailViewModel>(); services.AddTransient<SettingsViewModel>(); services.AddSingleton<MainWindow>(); services.AddTransient<SettingsWindow>(); Services = services.BuildServiceProvider(); var mainWindow = Services.GetRequiredService<MainWindow>(); mainWindow.DataContext = Services.GetRequiredService<MainViewModel>(); mainWindow.Show(); } }
3. ViewModel 构造函数注入 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 public partial class MainViewModel : ObservableRecipient { private readonly INoteService _noteService; private readonly IDialogService _dialogService; private readonly IServiceProvider _serviceProvider; public MainViewModel ( INoteService noteService, IDialogService dialogService, IServiceProvider serviceProvider, IMessenger messenger ) : base (messenger ) { _noteService = noteService; _dialogService = dialogService; _serviceProvider = serviceProvider; IsActive = true ; } [RelayCommand ] private void OpenNoteDetail (int noteId ) { var detailVm = _serviceProvider.GetRequiredService<NoteDetailViewModel>(); detailVm.LoadNote(noteId); CurrentViewModel = detailVm; } }
4. 工厂模式场景 有时需要运行参数创建 ViewModel,不能用纯构造注入:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 public class ViewModelFactory { private readonly IServiceProvider _sp; public ViewModelFactory (IServiceProvider sp ) => _sp = sp; public NoteDetailViewModel CreateNoteDetail (int noteId ) { var vm = _sp.GetRequiredService<NoteDetailViewModel>(); vm.LoadNote(noteId); return vm; } } services.AddSingleton<ViewModelFactory>(); services.AddTransient<Func<int , NoteDetailViewModel>>(sp => { return (noteId) => { var vm = sp.GetRequiredService<NoteDetailViewModel>(); vm.LoadNote(noteId); return vm; }; }); public class MainViewModel { private readonly Func<int , NoteDetailViewModel> _noteDetailFactory; public MainViewModel (Func<int , NoteDetailViewModel> noteDetailFactory ) { _noteDetailFactory = noteDetailFactory; } [RelayCommand ] private void OpenNote (int id ) { var vm = _noteDetailFactory(id); CurrentViewModel = vm; } }
5. 生命周期管理 1 2 3 4 5 6 7 8 9 services.AddSingleton<ICacheService, MemoryCacheService>(); services.AddTransient<NoteDetailViewModel>(); services.AddScoped<IScopedService, ScopedService>();
6. 使用 Ioc 辅助类 CommunityToolkit.Mvvm 内置了 Ioc 辅助类(DependencyInjection/Ioc.cs):
1 2 3 4 5 6 7 8 using CommunityToolkit.Mvvm.DependencyInjection;Ioc.Default.ConfigureServices(services.BuildServiceProvider()); var vm = Ioc.Default.GetRequiredService<MainViewModel>();
完整示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 public partial class App : Application { protected override void OnStartup (StartupEventArgs e ) { var services = new ServiceCollection(); services.AddSingleton<INoteService, NoteService>(); services.AddTransient<MainViewModel>(); services.AddSingleton<MainWindow>(); Ioc.Default.ConfigureServices(services.BuildServiceProvider()); var mainWindow = Ioc.Default.GetRequiredService<MainWindow>(); mainWindow.DataContext = Ioc.Default.GetRequiredService<MainViewModel>(); mainWindow.Show(); } } public class MainViewModel { public MainViewModel () { var noteService = Ioc.Default.GetRequiredService<INoteService>(); } }
7. 测试中使用 DI 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 public class IntegrationTests { private readonly IServiceProvider _services; public IntegrationTests () { var services = new ServiceCollection(); services.AddSingleton<IMessenger>(WeakReferenceMessenger.Default); services.AddSingleton<INoteService, NoteService>(); services.AddTransient<NoteListViewModel>(); services.AddTransient<NoteDetailViewModel>(); _services = services.BuildServiceProvider(); } [Fact ] public void NoteListViewModel_ShouldResolveAllDependencies () { var vm = _services.GetRequiredService<NoteListViewModel>(); Assert.NotNull(vm); } [Fact ] public void NoteDetail_Save_ShouldNavigateToList () { var messenger = _services.GetRequiredService<IMessenger>(); var vm = _services.GetRequiredService<NoteDetailViewModel>(); NavigateToListMessage? received = null ; messenger.Register<NavigateToListMessage>(this , (r, m) => received = m); vm.Title = "测试标题" ; vm.SaveCommand.Execute(null ); Assert.NotNull(received); } }
8. 综合模板 创建新项目时的推荐模板结构:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 public partial class App : Application { protected override void OnStartup (StartupEventArgs e ) { var services = new ServiceCollection(); services.AddSingleton<IMessenger>(WeakReferenceMessenger.Default); services.AddSingleton<IDataService, DataService>(); services.AddSingleton<IDialogService, DialogService>(); services.AddTransient<MainViewModel>(); services.AddTransient<DetailViewModel>(); services.AddSingleton<MainWindow>(); Ioc.Default.ConfigureServices(services.BuildServiceProvider()); var window = Ioc.Default.GetRequiredService<MainWindow>(); window.DataContext = Ioc.Default.GetRequiredService<MainViewModel>(); window.Show(); } }
1 2 3 4 5 6 7 8 public partial class MainViewModel : ObservableRecipient { public MainViewModel (IMessenger messenger ) : base (messenger ) { IsActive = true ; } }
1 2 3 4 5 6 <Application x:Class ="MyApp.App" xmlns ="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x ="http://schemas.microsoft.com/winfx/2006/xaml" > </Application >
下一篇预告:系列完结 — 从源码到实战全链路总结