如何用DI容器解析并初始化运行时创建的ViewModel
解决方案:在WPF+MVVM+Simple Injector中处理运行时创建ViewModel
针对你遇到的IDE式多标签场景,推荐以下几种符合DI原则的方案,避免手动new或服务定位器反模式:
1. 显式工厂模式(最推荐)
定义专门的工厂接口封装FileVm的创建逻辑,将工厂注入到MainWindowVm中,完全符合依赖倒置原则。
步骤示例:
- 定义工厂接口:
public interface IFileVmFactory { FileVm Create(FileData fileData); }
- 实现工厂(依赖由容器自动注入):
public class FileVmFactory : IFileVmFactory { private readonly IFileEditingService _editingService; private readonly IUndoRedoService _undoRedoService; // 工厂的依赖通过构造注入 public FileVmFactory(IFileEditingService editingService, IUndoRedoService undoRedoService) { _editingService = editingService; _undoRedoService = undoRedoService; } public FileVm Create(FileData fileData) { // 传入运行时文件数据+注入的服务,实例化FileVm return new FileVm(fileData, _editingService, _undoRedoService); } }
- 组合根注册:
// 工厂为无状态服务,注册为单例 container.Register<IFileVmFactory, FileVmFactory>(Lifestyle.Singleton); // FileVm每次打开都要新实例,注册为瞬时 container.Register<FileVm>(Lifestyle.Transient);
- MainWindowVm中使用:
public class MainWindowVm : ObservableObject { private readonly IFileVmFactory _fileVmFactory; public ObservableCollection<FileVm> OpenTabs { get; } = new(); public MainWindowVm(IFileVmFactory fileVmFactory) { _fileVmFactory = fileVmFactory; } public void OpenFile(FileData fileData) { var newTab = _fileVmFactory.Create(fileData); OpenTabs.Add(newTab); } }
2. 使用Simple Injector内置的Func工厂(简化版)
如果工厂逻辑简单,无需单独写工厂类,可以直接注册一个Func<FileData, FileVm>,让Simple Injector自动处理依赖解析:
组合根注册:
container.Register<FileVm>(Lifestyle.Transient); // 注册Func,将运行时FileData作为构造参数传入 container.RegisterInstance<Func<FileData, FileVm>>(fileData => container.GetInstance<FileVm>(new[] { fileData }));
MainWindowVm中使用:
public class MainWindowVm : ObservableObject { private readonly Func<FileData, FileVm> _fileVmCreator; public ObservableCollection<FileVm> OpenTabs { get; } = new(); public MainWindowVm(Func<FileData, FileVm> fileVmCreator) { _fileVmCreator = fileVmCreator; } public void OpenFile(FileData fileData) { var newTab = _fileVmCreator(fileData); OpenTabs.Add(newTab); } }
3. 结合MVVM Toolkit信使的优化
如果用信使传递"打开文件"的指令,只需在消息处理逻辑中调用上述工厂即可,信使仅作为事件触发机制,不参与ViewModel实例化:
// 定义打开文件的消息 public record OpenFileMessage(FileData TargetFile); // MainWindowVm中订阅消息 public MainWindowVm(IFileVmFactory fileVmFactory, IMessenger messenger) { _fileVmFactory = fileVmFactory; messenger.Register<OpenFileMessage>(this, (recipient, message) => recipient.OpenFile(message.TargetFile)); }
关键注意事项:
- 绝对避免在ViewModel中直接引用DI容器(服务定位器反模式)
FileVm必须注册为瞬时生命周期,确保每次打开文件都是独立实例- 运行时数据(如
FileData)属于上下文参数,不要通过DI容器注册,仅通过构造/方法参数传递
内容的提问来源于stack exchange,提问作者Lennart
相关产品推荐
相关产品推荐

