You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.27 02:55:32