CommunityToolkit.Mvvm学习笔记(7)——Ioc控制反转

一、引言

由于项目比较忙,所以很久没有学习Mvvm工具包了。
那为什么突然又学了呢,因为项目中遇到了一个问题——不同的ViewModel间该如何通信?
不过我并不确定Ioc与这个问题有什么关系,只是感觉可能有关系。在学之前,我稍微搜了下Ioc是什么,看到了它的一个齿轮模型,觉得很有意思,所以还是偷了个闲来学习了。


二、Ioc(Inversion of control)

*0. Ioc的形象理解

我先来解释一下Ioc是啥,然后再去学习官方文档的东西。
Ioc是Inversion Of Control的缩写,直译过来就是控制反转
这是什么奇怪的名字。
从一个齿轮例子说起,

为什么是齿轮呢?可能是大家都喜欢造轮子吧。
如果把程序看成一个现实中运转的机械系统,那你写的代码模块就像系统中转动的齿轮一样。

在这里插入图片描述
假设这三个齿轮组成了一个系统,显然A齿轮的转动会带动小齿轮B、C。
如果把该齿轮系统看作程序,这说明什么?
说明A和B、C之间存在依赖关系。再稍微专业一点就是,模块间存在耦合。
要使整个系统运转起来,就要一个齿轮转动来带动其他齿轮转动。先转动的那个齿轮掌握着主动权,即控制权。
模块间存在耦合不可避免,但实际开发中,我们往往更喜欢低耦合,这就得解耦。
所以我们能不能使这几个齿轮分开,达到解耦呢?
如果直接分开,一个齿轮转动就带动不了其他齿轮了,那系统就运转不起来了。
现在,我们可以在这几个齿轮之间再加入一个齿轮。
在这里插入图片描述
所以现在是A转动带动D,再由D带动B和C?
如果是这样,那还不如A直接带动B和C呢。
新加入的齿轮D其实是第三方的东西,在你的程序里你可以认为它是一个框架提供的机制或功能。
它才是一直在转动的一方。
D一直在转,你需要A时,就把A装到D上去,需要B时就把B装上去。这样你需要的功能就会跟着“转起来”。(要啥装啥,且不需要三者直接相关了,这样A、B、C之间不就解耦了么?不过这只是一个粗略的理解,具体怎样还得往后看)
对比第一张图的情况,原来是由你的齿轮先转动,带动其他几个齿轮。
现在你的齿轮都是被动的一方,都是由这个第三方的齿轮所带动的。
到这里,控制反转的意思应该呼之欲出了。主动权(或者说控制权)发生了转变。你由主动方变成了被动方。
有了上面的基本理解之后,来正式学习下Ioc。

1. Ioc

Ioc是一种常见的模式,用来增强(使用MVVM模式的)程序的模块性。

增加模块化程度的,并且是适用于使用MVVM的程序的。不要啥程序都Ioc。

Ioc最常见的实现方式是用依赖注入(DI,Dependency Injection),实现思路是创建大量的服务,并将服务注入到后端类中(如,作为参数传给ViewModel构造函数)——这使得使用这些服务的代码不再依赖于服务的实现细节,并能很容易替换服务的具体实现。

使用DI,模块间不再直接依赖,它们之间的代码不再耦合在一起。你更换一侧服务的细节实现,那调用侧并不用做更改,因为对它来说,我还是调用这个服务,至于内部发生了什么变化并不关心。

这种模式还可以很容易地将平台特有的特性提供给后端代码,方法是通过服务抽象它们,然后注入到需要的地方。

MVVM工具包不提供内置的API来促进该模式使用,因为已经存在针对这种模式的专用库了,比如 Microsoft.Extensions.DependencyInjection 包,该包提供了一个功能齐全、功能强大的DI API集,并通过一个易于安装和使用的工具—— IServiceProvider 起作用。下面介绍会引用该库,并提供一系列示例来说明如何将它集成到MVVM模式的程序中。

API集:Ioc

2. 配置和解析服务

第一步是声明一个 IServiceProvider 实例,并初始化所有需要的服务,通常是在程序启动时做这些事。例如,在UWP上(类似设置也可用于其他框架):

public sealed partial class App : Application
{
    public App()
    {
        Services = ConfigureServices();

        this.InitializeComponent();
    }

    /// <summary>
    /// Gets the current <see cref="App"/> instance in use
    /// </summary>
    public new static App Current => (App)Application.Current;

    /// <summary>
    /// Gets the <see cref="IServiceProvider"/> instance to resolve application services.
    /// </summary>
    public IServiceProvider Services { get; }

    /// <summary>
    /// Configures the services for the application.
    /// </summary>
    private static IServiceProvider ConfigureServices()
    {
        var services = new ServiceCollection();

        services.AddSingleton<IFilesService, FilesService>();
        services.AddSingleton<ISettingsService, SettingsService>();
        services.AddSingleton<IClipboardService, ClipboardService>();
        services.AddSingleton<IShareService, ShareService>();
        services.AddSingleton<IEmailService, EmailService>();

        return services.BuildServiceProvider();
    }
}

本例中,Services 属性在启动时被初始化,并且所有的应用程序服务和viewmodel都被注册。还有一个新的 Current 属性,该属性可用于从程序的其他view中轻松访问 Services 属性。例如:

IFilesService filesService = App.Current.Services.GetService<IFilesService>();

// Use the files service here...

这里关键的点是,每个服务都能很好地使用特定于平台的API,但由于这些API都是通过我们代码使用的接口抽象出来的,在解析实例和用它执行操作时,所以我们不需要关心它们。

3. 构造函数注入

还有一个强大的特性是“构造函数注入(constructor injection)”,这意味着DI(依赖注入)服务提供程序能在创建被请求类的实例时自动解析已注册服务之间的间接依赖关系。考虑以下服务:

public class FileLogger : IFileLogger
{
    private readonly IFilesService FileService;
    private readonly IConsoleService ConsoleService;

    public FileLogger(
        IFilesService fileService,
        IConsoleService consoleService)
    {
        FileService = fileService;
        ConsoleService = consoleService;
    }

    // Methods for the IFileLogger interface here...
}

这里我们有一个实现了 IFileLogger 接口的 FileLogger 类,并且需要 IFilesServiceIConsoleService 实例。构造函数注入意味着DI服务提供程序会自动收集所有必要的服务,就像这样:

/// <summary>
/// Configures the services for the application.
/// </summary>
private static IServiceProvider ConfigureServices()
{
    var services = new ServiceCollection();

    services.AddSingleton<IFilesService, FilesService>();
    services.AddSingleton<IConsoleService, ConsoleService>();
    services.AddSingleton<IFileLogger, FileLogger>();

    return services.BuildServiceProvider();
}

// Retrieve a logger service with constructor injection
IFileLogger fileLogger = App.Current.Services.GetService<IFileLogger>();

DI服务提供程序会自动检查是否已经注册所有需要的服务,然后会检索它们并调用已注册的 IFileLogger 具体类的构造函数,来获取返回的实例。

4. ViewModel

服务提供程序的名称中有“服务”两字,但实际上,它可以用来解析任何类的实例,包括ViewModel!上文提到的概念仍然适用,包括构造函数注入。假设我们有一个ContactsViewModel 类,通过它的构造函数使用一个 IContactsServiceIPhoneService 实例。我们可以用一个 ConfigureServices 方法,就像这样:

/// <summary>
/// Configures the services for the application.
/// </summary>
private static IServiceProvider ConfigureServices()
{
    var services = new ServiceCollection();

    // Services
    services.AddSingleton<IContactsService, ContactsService>();
    services.AddSingleton<IPhoneService, PhoneService>();

    // Viewmodels
    services.AddTransient<ContactsViewModel>();

    return services.BuildServiceProvider();
}

接着在 ContactsView 中,分配如下的数据上下文:

public ContactsView()
{
    this.InitializeComponent();
    this.DataContext = App.Current.Services.GetService<ContactsViewModel>();
}

三、结尾

本文中应用型的内容不多,几个基本用法的示例很难让你对(Ioc)DI的应用场景及具体用法有较清晰的认识。
因为在MVVM Toolkit的官方文档里对DI的描述就是这么简略(这本来就是讲Ioc的章节),想要更深入了解DI的应用,得看DI的专门章节。
所以我觉得本文把Ioc的概念给弄懂就好,并且几个基本示例可以再结合网上别人的例子看看具体如何使用,不至于之后正式用时感到陌生。

  • 1
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: communitytoolkit.mvvm datagrid 是一种基于MVVM模式的数据表格控件,该控件提供了多种数据绑定的方式,可以快速且简便地展示数据。同时,communitytoolkit.mvvm datagrid 还提供了多种功能,如单元格编辑、分页、排序、筛选等等。这让用户能够方便地处理大量数据。 在使用 communitytoolkit.mvvm datagrid 控件时,需要绑定数据源并提供列的定义,控件会自动将数据填充到表格中。在提供列定义时,可以对不同列的排序方式、列宽、列标题等进行自定义的设置。此外,还可以对每个单元格进行自定义的数据格式化。 与传统的数据表格控件不同的是,communitytoolkit.mvvm datagrid 控件遵循 MVVM 模式的设计,使得数据与 UI 分离,易于维护和扩展。通过将数据、业务逻辑与界面之间的关系明确分离,可以大大提高应用程序的可读性、可扩展性和可维护性。 总的来说,communitytoolkit.mvvm datagrid 是一款功能强大且易于使用的数据表格控件,通过采用 MVVM 模式,可以使数据与 UI 分离,极大地提高应用程序的可读性、可扩展性和可维护性。无论是处理大量数据、展示分页数据、自定义数据格式化等,都是 communitytoolkit.mvvm datagrid 优秀的解决方案。 ### 回答2: CommunityToolkit.Mvvm 是一个帮助开发人员实现 MVVM 模式在 UWP 和 WPF 应用程序中的工具包。这个工具包提供了丰富的控件和类库,使得我们能够更加方便地使用 MVVM 模式来构建应用程序。 其中,DataGrid 控件是 CommunityToolkit.Mvvm 中非常重要的一个控件,它允许我们以表格的形式展示数据,并且支持排序、筛选、列隐藏等基本功能。除此之外,CommunityToolkit.Mvvm 的 DataGrid 还支持分页和自定义单元格样式等高级功能,这些功能都使得我们更加容易构建强大可靠的应用程序。 在使用 CommunityToolkit.Mvvm 的 DataGrid 控件时,我们需要定义一些属性和命令,在 ViewModel 中实现数据的加载、数据的操作等逻辑处理。具体而言,我们需要定义 DataGrid 的 ItemsSource 属性,这个属性绑定到 ViewModel 中的数据集合,然后定义排序、筛选等命令,使得用户点击控件时能够实现相应的逻辑操作。 总之,CommunityToolkit.Mvvm 提供的 DataGrid 控件是一个非常实用的控件,它能够大大简化我们构建 MVVM 应用程序的过程。通过这个控件,我们能够快速轻松地展现数据,同时也能够支持高级功能,使得我们能够更加专注于应用程序的逻辑设计和开发。 ### 回答3: communitytoolkit.mvvm datagrid 是一个开源的工具包,旨在为使用 MVVM 模式的开发人员提供一个易于使用和高度可定制的数据网格。该工具包是由 Microsoft 社区开发的,可以使用 NuGet 包管理器轻松安装和集成到您的应用程序中。 该工具包使用 C# 和 XAML 构建,支持 WPF 和 UWP 平台。它提供了许多有用的功能,如排序、筛选和分页,使用户能够更轻松地浏览和操作数据。此外,它还提供了高度可定制的外观和行为选项,以便开发人员可以使其适应他们的应用程序的视觉设计和用户体验。 与传统的数据网格不同,使用 communitytoolkit.mvvm datagrid 可以使用 MVVM 模式进行数据绑定和操作,将视图逻辑与数据逻辑分离。这使得应用程序更易于维护和扩展,并提高了代码的可重用性和可测试性。此外,它还提供了一个非常灵活的 API,使用户可以轻松地自定义和扩展其功能和行为。 总的来说,communitytoolkit.mvvm datagrid 是一个非常有用的工具包,为开发人员提供了一个高度可定制的数据网格,支持 MVVM 模式,使其更易于使用和维护。如果你正在开发一个需要展示和操作数据的应用程序,那么这个工具包是值得一试的。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值