如何在 WPF/MVVM 应用程序中处理依赖注入

Posted

技术标签:

【中文标题】如何在 WPF/MVVM 应用程序中处理依赖注入【英文标题】:How to handle dependency injection in a WPF/MVVM application 【发布时间】:2014-10-11 13:01:32 【问题描述】:

我正在启动一个新的桌面应用程序,我想使用 MVVM 和 WPF 构建它。

我也打算使用 TDD。

问题是我不知道应该如何使用 IoC 容器将我的依赖项注入到我的生产代码中。

假设我有以下类和接口:

public interface IStorage

    bool SaveFile(string content);


public class Storage : IStorage

    public bool SaveFile(string content)
        // Saves the file using StreamWriter
    

然后我有另一个类具有 IStorage 作为依赖项,还假设这个类是 ViewModel 或业务类...

public class SomeViewModel

    private IStorage _storage;

    public SomeViewModel(IStorage storage)
        _storage = storage;
    

有了这个我可以很容易地编写单元测试,以确保它们正常工作,使用模拟等。

问题在于在实际应用程序中使用它时。我知道我必须有一个 IoC 容器来链接 IStorage 接口的默认实现,但我该怎么做呢?

例如,如果我有以下 xaml 会怎样:

<Window 
    ... xmlns definitions ...
>
   <Window.DataContext>
        <local:SomeViewModel />
   </Window.DataContext>
</Window>

在这种情况下,如何正确“告诉”WPF 注入依赖项?

另外,假设我需要一个来自 C# 代码的 SomeViewModel 实例,我应该怎么做?

我觉得我完全迷失了,我会很感激任何关于如何处理它的最佳方式的例子或指导。

我熟悉 StructureMap,但我不是专家。另外,如果有更好/更简单/开箱即用的框架,请告诉我。

【问题讨论】:

使用 .net core 3.0 预览版,您可以使用一些 Microsoft nuget 包来实现。 【参考方案1】:

我采用“视图优先”的方法,将视图模型传递给视图的构造函数(在其代码隐藏中),该构造函数被分配给数据上下文,例如

public class SomeView

    public SomeView(SomeViewModel viewModel)
    
        InitializeComponent();

        DataContext = viewModel;
    

这取代了您基于 XAML 的方法。

我使用 Prism 框架来处理导航 - 当某些代码请求显示特定视图时(通过“导航”到它),Prism 将解析该视图(在内部,使用应用程序的 DI 框架); DI 框架将依次解析视图具有的任何依赖项(我的示例中的视图模型),然后解析 依赖项,依此类推。

DI 框架的选择几乎无关紧要,因为它们基本上都做同样的事情,即您注册一个接口(或一个类型)以及您希望框架在发现对该接口的依赖时实例化的具体类型.为了记录,我使用的是温莎城堡。

Prism 导航需要一些时间来适应,但是一旦你开始熟悉它,它就非常好,它允许你使用不同的视图来编写你的应用程序。例如。您可以在主窗口上创建一个 Prism“区域”,然后使用 Prism 导航在该区域内从一个视图切换到另一个视图,例如当用户选择菜单项或其他内容时。

或者看看其中一个 MVVM 框架,例如 MVVM Light。我没有这方面的经验,所以无法评论他们喜欢用什么。

【讨论】:

如何将构造函数参数传递给子视图?我已经尝试过这种方法,但是在父视图中出现异常,告诉我子视图没有默认的无参数构造函数【参考方案2】:

安装 MVVM Light。

安装的一部分是创建视图模型定位器。这是一个将您的视图模型公开为属性的类。然后可以从您的 IOC 引擎返回这些属性的 getter。幸运的是,MVVM light 还包含 SimpleIOC 框架,但您可以根据需要连接其他框架。

使用简单的 IOC,您可以针对类型注册实现...

SimpleIOC.Default.Register<MyViewModel>(()=> new MyViewModel(new ServiceProvider()), true);

在此示例中,您的视图模型被创建并根据其构造函数传递了一个服务提供者对象。

然后您创建一个属性,该属性从 IOC 返回一个实例。

public MyViewModel

    get  return SimpleIOC.Default.GetInstance<MyViewModel>; 

巧妙的是,视图模型定位器随后在 app.xaml 或等效的数据源中创建。

<local:ViewModelLocator x:key="Vml" />

您现在可以绑定到它的“MyViewModel”属性,以通过注入的服务获取您的视图模型。

希望对您有所帮助。对任何代码不准确表示歉意,从 iPad 上的内存中编码。

【讨论】:

您不应该在应用程序的引导程序之外有GetInstanceresolve。这就是 DI 的意义所在! 我同意你可以在启动期间设置属性值,但是建议使用惰性实例化是针对 DI 是错误的。 @kishaw 我没有。【参考方案3】:

使用Managed Extensibility Framework。

[Export(typeof(IViewModel)]
public class SomeViewModel : IViewModel

    private IStorage _storage;

    [ImportingConstructor]
    public SomeViewModel(IStorage storage)
        _storage = storage;
    

    public bool ProperlyInitialized  get  return _storage != null;  


[Export(typeof(IStorage)]
public class Storage : IStorage

    public bool SaveFile(string content)
        // Saves the file using StreamWriter
    


//Somewhere in your application bootstrapping...
public GetViewModel() 
     //Search all assemblies in the same directory where our dll/exe is
     string currentPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
     var catalog = new DirectoryCatalog(currentPath);
     var container = new CompositionContainer(catalog);
     var viewModel = container.GetExport<IViewModel>();
     //Assert that MEF did as advertised
     Debug.Assert(viewModel is SomViewModel); 
     Debug.Assert(viewModel.ProperlyInitialized);

一般来说,你会做的是有一个静态类并使用工厂模式为你提供一个全局容器(缓存,natch)。

至于如何注入视图模型,您注入它们的方式与注入其他所有内容的方式相同。在 XAML 文件的代码隐藏中创建导入构造函数(或在属性/字段上放置导入语句),并告诉它导入视图模型。然后将您的WindowDataContext 绑定到该属性。您自己实际从容器中拉出的根对象通常由 Window 对象组成。只需将接口添加到窗口类,然后导出它们,然后从上面的目录中获取(在 App.xaml.cs 中......这是 WPF 引导文件)。

【讨论】:

您遗漏了 DI 的一个重要点,即避免使用 new 创建任何实例。【参考方案4】:

我建议使用 ViewModel - 第一种方法 https://github.com/Caliburn-Micro/Caliburn.Micro

见: https://caliburnmicro.codeplex.com/wikipage?title=All%20About%20Conventions

使用Castle Windsor 作为 IOC 容器。

所有关于约定

Caliburn.Micro 的主要功能之一体现在它能够通过执行一系列约定来消除对样板代码的需求。有些人喜欢公约,有些人讨厌它们。这就是为什么 CM 的约定是完全可定制的,如果不需要,甚至可以完全关闭。如果您要使用约定,并且由于默认情况下它们处于打开状态,那么最好了解这些约定是什么以及它们是如何工作的。这就是本文的主题。 视图分辨率(ViewModel-First)

基础知识

您在使用 CM 时可能遇到的第一个约定与视图分辨率有关。此约定会影响应用程序的任何 ViewModel-First 区域。在 ViewModel-First 中,我们有一个需要渲染到屏幕的现有 ViewModel。为此,CM 使用一个简单的命名模式来查找应绑定到 ViewModel 并显示的 UserControl1。那么,这种模式是什么?让我们看看 ViewLocator.LocateForModelType 就知道了:

public static Func<Type, DependencyObject, object, UIElement> LocateForModelType = (modelType, displayLocation, context) =>
    var viewTypeName = modelType.FullName.Replace("Model", string.Empty);
    if(context != null)
    
        viewTypeName = viewTypeName.Remove(viewTypeName.Length - 4, 4);
        viewTypeName = viewTypeName + "." + context;
    

    var viewType = (from assmebly in AssemblySource.Instance
                    from type in assmebly.GetExportedTypes()
                    where type.FullName == viewTypeName
                    select type).FirstOrDefault();

    return viewType == null
        ? new TextBlock  Text = string.Format("0 not found.", viewTypeName) 
        : GetOrCreateViewType(viewType);
;

首先让我们忽略“上下文”变量。为了派生视图,我们假设您在命名 VM 时使用文本“ViewModel”,因此我们只需通过删除“Model”一词将其更改为“View”。这具有更改类型名称和名称空间的效果。所以 ViewModels.CustomerViewModel 会变成 Views.CustomerView。或者,如果您按功能组织应用程序:CustomerManagement.CustomerViewModel 变为 CustomerManagement.CustomerView。希望这很简单。一旦我们有了名称,我们就可以搜索具有该名称的类型。我们通过 AssemblySource.Instance.2 将您暴露给 CM 的任何程序集搜索为可搜索的。如果我们找到该类型,我们将创建一个实例(如果已注册,则从 IoC 容器中获取一个实例)并将其返回给调用者。如果我们没有找到该类型,我们会生成一个带有适当“未找到”消息的视图。

现在,回到那个“上下文”值。这就是 CM 在同一个 ViewModel 上支持多个 View 的方式。如果提供了上下文(通常是字符串或枚举),我们会根据该值对名称进行进一步的转换。这种转换有效地假设您通过从末尾删除单词“View”并附加上下文来为不同的视图创建一个文件夹(命名空间)。因此,给定“Master”上下文,我们的 ViewModels.CustomerViewModel 将变为 Views.Customer.Master。

【讨论】:

你的整个帖子都是意见。【参考方案5】:

在您的问题中,您在 XAML 中设置了视图的 DataContext 属性的值。这要求您的视图模型具有默认构造函数。但是,正如您所指出的,这不适用于要在构造函数中注入依赖项的依赖项注入。

所以您不能在 XAML 中设置 DataContext 属性,也不能进行依赖注入。相反,您还有其他选择。

如果您的应用程序基于简单的分层视图模型,您可以在应用程序启动时构建整个视图模型层次结构(您必须从 App.xaml 文件中删除 StartupUri 属性):

public partial class App 

  protected override void OnStartup(StartupEventArgs e) 
    base.OnStartup(e);
    var container = CreateContainer();
    var viewModel = container.Resolve<RootViewModel>();
    var window = new MainWindow  DataContext = viewModel ;
    window.Show();
  


这是基于以RootViewModel 为根的视图模型的对象图,但您可以将一些视图模型工厂注入父视图模型,从而允许它们创建新的子视图模型,因此对象图没有等待修复。这也有望回答您的问题假设我需要来自 cs 代码的 SomeViewModel 实例,我应该怎么做?

class ParentViewModel 

  public ParentViewModel(ChildViewModelFactory childViewModelFactory) 
    _childViewModelFactory = childViewModelFactory;
  

  public void AddChild() 
    Children.Add(_childViewModelFactory.Create());
  

  ObservableCollection<ChildViewModel> Children  get; private set; 

 

class ChildViewModelFactory 

  public ChildViewModelFactory(/* ChildViewModel dependencies */) 
    // Store dependencies.
  

  public ChildViewModel Create() 
    return new ChildViewModel(/* Use stored dependencies */);
  


如果您的应用程序本质上更具动态性,并且可能基于导航,您将不得不挂钩执行导航的代码。每次导航到新视图时,您需要创建视图模型(从 DI 容器)、视图本身并将视图的 DataContext 设置为视图模型。您可以在view first根据视图选择视图模型,也可以view-model first在视图模型确定要使用的视图的情况下执行此操作. MVVM 框架提供了这个关键功能,您可以通过某种方式将 DI 容器连接到视图模型的创建中,但您也可以自己实现它。我在这里有点含糊,因为根据您的需要,此功能可能会变得非常复杂。这是您从 MVVM 框架中获得的核心功能之一,但是在一个简单的应用程序中滚动您自己的功能将使您很好地了解 MVVM 框架在幕后提供的功能。

由于无法在 XAML 中声明 DataContext,您将失去一些设计时支持。如果您的视图模型包含一些数据,它将在设计时出现,这非常有用。幸运的是,您也可以在 WPF 中使用design-time attributes。一种方法是将以下属性添加到 XAML 中的 &lt;Window&gt; 元素或 &lt;UserControl&gt;

xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DataContext="d:DesignInstance Type=local:MyViewModel, IsDesignTimeCreatable=True"

视图模型类型应该有两个构造函数,一个用于设计时数据的默认构造函数,另一个用于依赖注入:

class MyViewModel : INotifyPropertyChanged 

  public MyViewModel() 
    // Create some design-time data.
  

  public MyViewModel(/* Dependencies */) 
    // Store dependencies.
  


通过这样做,您可以使用依赖注入并保持良好的设计时支持。

【讨论】:

这正是我想要的。有多少次我读到“只需使用 [yadde-ya] 框架”的答案让我感到沮丧。这一切都很好,但我想知道如何自己首先推出它,然后我才能知道哪种框架实际上可能对我有用。谢谢你写得这么清楚。 “你不能在 XAML 中设置 DataContext 属性”这是完全错误的 @Shenron 请阅读前面的段落:这要求您的视图模型具有默认构造函数。 为避免进一步混淆,我做了一个小的编辑以澄清它是在 XAML 中设置数据上下文和不起作用的依赖注入的组合。 @MartinLiversage 我仍然不同意,依赖注入不会阻止在 xaml 中设置数据上下文。见***.com/questions/54877352/… @Shenron 我的观点是,如果视图模型没有默认构造函数,则无法在 XAML“通用 WPF 方式”中设置数据上下文。我知道你不喜欢我的选择,但我仍然认为这是一个有效的观点。问题是如何处理这个问题。这里有多个答案,您的链接答案通过使用自定义标记扩展提供了替代解决方案。您仍然必须想出解决问题的方法,而这正是 Stack Overflow 的意义所在:为问题提供解决方案。【参考方案6】:

我一直在使用 Ninject,发现使用起来很愉快。一切都在代码中设置,语法相当简单,并且有很好的文档(以及大量关于 SO 的答案)。

所以基本上是这样的:

创建视图模型,并将IStorage接口作为构造函数参数:

class UserControlViewModel

    public UserControlViewModel(IStorage storage)
    

    

为视图模型创建一个带有 get 属性的 ViewModelLocator,它从 Ninject 加载视图模型:

class ViewModelLocator

    public UserControlViewModel UserControlViewModel
    
        get  return IocKernel.Get<UserControlViewModel>(); // Loading UserControlViewModel will automatically load the binding for IStorage
    

在 App.xaml 中将 ViewModelLocator 设为应用程序范围的资源:

<Application ...>
    <Application.Resources>
        <local:ViewModelLocator x:Key="ViewModelLocator"/>
    </Application.Resources>
</Application>

UserControlDataContext绑定到ViewModelLocator中的对应属性。

<UserControl ...
             DataContext="Binding UserControlViewModel, Source=StaticResource ViewModelLocator">
    <Grid>
    </Grid>
</UserControl>

创建一个继承 NinjectModule 的类,它将设置必要的绑定(IStorage 和视图模型):

class IocConfiguration : NinjectModule

    public override void Load()
    
        Bind<IStorage>().To<Storage>().InSingletonScope(); // Reuse same storage every time

        Bind<UserControlViewModel>().ToSelf().InTransientScope(); // Create new instance every time
    

在应用程序启动时使用必要的 Ninject 模块(目前为上述模块)初始化 IoC 内核:

public partial class App : Application
       
    protected override void OnStartup(StartupEventArgs e)
    
        IocKernel.Initialize(new IocConfiguration());

        base.OnStartup(e);
    

我使用了一个静态 IocKernel 类来保存 IoC 内核的应用程序范围实例,因此我可以在需要时轻松访问它:

public static class IocKernel

    private static StandardKernel _kernel;

    public static T Get<T>()
    
        return _kernel.Get<T>();
    

    public static void Initialize(params INinjectModule[] modules)
    
        if (_kernel == null)
        
            _kernel = new StandardKernel(modules);
        
    

这个解决方案确实使用了静态ServiceLocatorIocKernel),这通常被认为是一种反模式,因为它隐藏了类的依赖关系。然而,要避免对 UI 类进行某种手动服务查找是非常困难的,因为它们必须有一个无参数的构造函数,并且无论如何您都无法控制实例化,因此您无法注入 VM。至少这种方式可以让你单独测试虚拟机,这是所有业务逻辑所在的地方。

如果有人有更好的方法,请分享。

编辑: Lucky Likey 通过让 Ninject 实例化 UI 类,提供了摆脱静态服务定位器的答案。答案详情可见here

【讨论】:

我是依赖注入的新手,但从本质上讲,您的解决方案是将 Service Locator 反模式与 Ninject 相结合,因为您使用的是静态 ViewModel Locator。有人可能会争辩说,注入是在 Xaml 文件中完成的,这种文件不太可能被测试。我没有更好的解决方案,可能会使用您的解决方案 - 但我认为在答案中提及这一点也会有所帮助。 伙计,您的解决方案很棒,只有一个“问题”与以下行:DataContext="Binding [...]"。这导致 VS-Designer 执行 ViewModel 的构造函数中的所有程序代码。在我的情况下,Window 正在执行并以模态方式阻止与 VS 的任何交互。也许应该修改 ViewModelLocator 以不在设计时定位“真正的” ViewModel。 - 另一个解决方案是“禁用项目代码”,这也将阻止显示其他所有内容。也许您已经找到了一个巧妙的解决方案。在这种情况下,我希望你能展示出来。 @LuckyLikey 您可以尝试使用 d:DataContext="d:DesignInstance vm:UserControlViewModel, IsDesignTimeCreatable=True" 但我不确定它是否会有所作为。但是 VM 构造函数为什么/如何启动模态窗口?什么样的窗户? @son 实际上我不知道为什么以及如何,但是当我从解决方案资源管理器中打开窗口设计器时,随着新选项卡的打开,窗口正在由设计器和与调试模式一样的窗口出现在 VS“Micorosoft Visual Studio XAML Designer”之外的新进程中。如果该进程被关闭,VS-Designer 也会因前面提到的异常而失败。我要试试你的解决方法。当我检测到新信息时,我会通知你 :) @sondergard 我已经发布了对您的答案的改进,避免了 ServiceLocator 反模式。请随意查看。【参考方案7】:

从您的 app.xaml 中删除启动 uri。

App.xaml.cs

public partial class App

    protected override void OnStartup(StartupEventArgs e)
    
        IoC.Configure(true);

        StartupUri = new Uri("Views/MainWindowView.xaml", UriKind.Relative);

        base.OnStartup(e);
    

现在您可以使用您的 IoC 类来构造实例。

MainWindowView.xaml.cs

public partial class MainWindowView

    public MainWindowView()
    
        var mainWindowViewModel = IoC.GetInstance<IMainWindowViewModel>();

        //Do other configuration            

        DataContext = mainWindowViewModel;

        InitializeComponent();
    


【讨论】:

您不应该在 app.xaml.cs 之外有任何容器 GetInstanceresolve,您将失去 DI 的意义。此外,在视图的代码隐藏中提及 xaml 视图有点令人费解。只需在纯 c# 中调用视图,然后使用容器执行此操作。【参考方案8】:

我在这里发布的是对 Sondergard 答案的改进,因为我要说的内容不适合评论 :)

事实上,我正在介绍一个简洁的解决方案,它避免了 ServiceLocatorStandardKernel-Instance 的包装器,在 Sondergard 的解决方案中称为 IocContainer。为什么?如前所述,这些都是反模式。

StandardKernel 随处可用

Ninject 魔法的关键是使用.Get&lt;T&gt;()-Method 所需的StandardKernel-Instance。

除了sondergard 的IocContainer,您还可以在App-Class 中创建StandardKernel

只需从您的 App.xaml 中删除 StartUpUri

<Application x:Class="Namespace.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
             ... 
</Application>

这是 App.xaml.cs 中应用的 CodeBehind

public partial class App

    private IKernel _iocKernel;

    protected override void OnStartup(StartupEventArgs e)
    
        base.OnStartup(e);

        _iocKernel = new StandardKernel();
        _iocKernel.Load(new YourModule());

        Current.MainWindow = _iocKernel.Get<MainWindow>();
        Current.MainWindow.Show();
    

从现在开始,Ninject 还活着,准备战斗:)

注入你的DataContext

由于 Ninject 还活着,您可以执行各种注入,例如 Property Setter Injection 或最常见的一种 Constructor Injection

这就是将 ViewModel 注入 WindowDataContext 的方式

public partial class MainWindow : Window

    public MainWindow(MainWindowViewModel vm)
    
        DataContext = vm;
        InitializeComponent();
    

当然,如果你做了正确的绑定,你也可以注入 IViewModel,但这不是这个答案的一部分。

直接访问内核

如果您需要直接调用内核上的方法(例如.Get&lt;T&gt;()-Method), 你可以让内核自己注入。

    private void DoStuffWithKernel(IKernel kernel)
    
        kernel.Get<Something>();
        kernel.Whatever();
    

如果您需要内核的本地实例,您可以将其作为属性注入。

    [Inject]
    public IKernel Kernel  private get; set; 

尽管这可能非常有用,但我不建议您这样做。请注意,以这种方式注入的对象在构造函数中不可用,因为它是稍后注入的。

根据这个link,您应该使用工厂扩展而不是注入IKernel(DI 容器)。

在软件系统中使用 DI 容器的推荐方法是应用程序的组合根是直接接触容器的单一位置。

如何使用 Ninject.Extensions.Factory 也可以是红色的here。

【讨论】:

不错的方法。从未探索过 Ninject 到这个级别,但我可以看到我错过了 :) 如果有人对如何使用Ninject.Extensions.Factory 感兴趣,请在 cmets 中说明,我会添加更多信息。 @LuckyLikey:如何通过没有无参数构造函数的 XAML 将 ViewModel 添加到窗口数据上下文?使用带有 ServiceLocator 的 sondergard 解决方案,这种情况是可能的。 那么请告诉我如何在附加属性中检索我需要的服务?它们始终是静态的,支持 DependencyProperty 字段及其 Get 和 Set 方法。 @springy76 一个有趣的问题。我知道,我来晚了,但你最后做了什么?在大多数情况下,我从来没有在我的附加属性中使用 DI。可能这就是您无法避免使用服务定位器的地方?【参考方案9】:

Canonic DryIoc 案例

回答一个旧帖子,但使用DryIoc 进行此操作,并做我认为很好地使用 DI 和接口的事情(最少使用具体类)。

    WPF 应用程序的起点是App.xaml,我们会在此处说明要使用的初始视图是什么;我们使用后面的代码而不是默认的 xaml 来做到这一点: 删除 App.xaml 中的 StartupUri="MainWindow.xaml"

    在代码隐藏 (App.xaml.cs) 中添加 override OnStartup:

    protected override void OnStartup(StartupEventArgs e)
    
        base.OnStartup(e);
        DryContainer.Resolve<MainWindow>().Show();
    
    

这就是起点;这也是唯一应该调用resolve 的地方。

    配置根(根据 Mark Seeman 的书 .NET 中的依赖注入;唯一应该提及具体类的地方)将位于构造函数中的相同代码隐藏中:

    public Container DryContainer  get; private set; 
    
    public App()
    
        DryContainer = new Container(rules => rules.WithoutThrowOnRegisteringDisposableTransient());
        DryContainer.Register<IDatabaseManager, DatabaseManager>();
        DryContainer.Register<IJConfigReader, JConfigReader>();
        DryContainer.Register<IMainWindowViewModel, MainWindowViewModel>(
            Made.Of(() => new MainWindowViewModel(Arg.Of<IDatabaseManager>(), Arg.Of<IJConfigReader>())));
        DryContainer.Register<MainWindow>();
    
    

备注和更多细节

我只在视图MainWindow 中使用了具体类; 我必须为 ViewModel 指定要使用的构造函数(我们需要使用 DryIoc 执行此操作),因为 XAML 设计器需要存在默认构造函数,而带有注入的构造函数是应用程序实际使用的构造函数。

带有 DI 的 ViewModel 构造函数:

public MainWindowViewModel(IDatabaseManager dbmgr, IJConfigReader jconfigReader)

    _dbMgr = dbmgr;
    _jconfigReader = jconfigReader;

ViewModel 设计的默认构造函数:

public MainWindowViewModel()


视图的代码隐藏:

public partial class MainWindow

    public MainWindow(IMainWindowViewModel vm)
    
        InitializeComponent();
        ViewModel = vm;
    

    public IViewModel ViewModel
    
        get  return (IViewModel)DataContext; 
        set  DataContext = value; 
    

以及在视图 (MainWindow.xaml) 中需要什么来获取带有 ViewModel 的设计实例:

d:DataContext="d:DesignInstance local:MainWindowViewModel, IsDesignTimeCreatable=True"

结论

因此,我们使用 DryIoc 容器和 DI 实现了 WPF 应用程序的非常简洁和最小化,同时保持视图和视图模型的设计实例成为可能。

【讨论】:

以上是关于如何在 WPF/MVVM 应用程序中处理依赖注入的主要内容,如果未能解决你的问题,请参考以下文章

wpf mvvm模式 Icommand接口应该如何理解?

WPF + MVVM + RadioButton:使用单个属性处理绑定

WPF MVVM:如何根据事件更新 UI 控制器

IOC 控制反转Android 事件依赖注入 ( 事件依赖注入代码示例 )

如何在 WPF MVVM 应用程序中成功登录关闭 LoginView?

如何在WPF中动态创建布局(MVVM模式)