C#中WPF实现依赖注入和MVVM,以及服务定位ServiceLocator

最近在想重写架构于是就研究了一套WPF的相关内容,WPF不像MAUI内置了容器,需要我们自己手动添加,于是就有了今天的内容。

首先,我们新建一个.net6.0的WPF项目

  1. 由于WPF没有内置容器,我们先安装一下依赖注入的nuget包

Microsoft.Extensions.DependencyInjection

点开App.xaml文件,修改为如下

public partial class App : Application 
{
    /// <summary>
    /// Gets the current <see cref="App"/> instance in use
    /// </summary>
    public new static App Current => (App) Application.Current;
}
  1. 由于我们要使用服务定位模式,所以在主项目上添加一个ServiceLocator.cs类
public class ServiceLocator 
{
    private IServiceProvider _serviceProvider;

    public ServiceLocator() 
    {
        var serviceCollection = new ServiceCollection();

        _serviceProvider = serviceCollection.BuildServiceProvider();
    }
}

这时我们就使用了容器,等待类型注入,此时我们的依赖注入初步就完成了,下面进行添加MVVM模式

  1. 安装MVVVM的nuget包

CommunityToolkit.Mvvm

安装完成后,我们开始新建按照MVVM的规则的文件夹,便于我们以后分层
如下图所示
在这里插入图片描述

  1. 在Models文件夹建一个Person类
public class Person 
{
    public int Id { get; set; } = 1;
    public string Name { get; set; } = "小米";
    
}

在ViewModels文件夹建一个MainWindowViewModel类

public class MainWindowViewModel : ObservableObject
{
    
}

使用MVVM模式的精髓就在于这个接口ObservableObject
MVVM的原理我们这里不做讲解,这里我们只讲怎么使用,优雅且自信
首先声明一下Person类

public partial class MainWindowViewModel : ObservableObject 
{
    [ObservableProperty]
    private Person _person;
}

这里的ObservableProperty实际帮我们自动写成了如下,并且class关键词前面要添加partial关键字,我们作为懒人肯定是不能写这么多的

private Person _person;
public Person Person {
    get => _person;
    set=> SetProperty(ref _person, value);
}

这时候我们出现了一个问题,这里我们要用依赖注入,那我们先去Service文件夹新建一个接口和实体类,我姑且叫做DataAccess
代码和结构如下图
在这里插入图片描述

public class DataAccess : IDataAccess 
{
    public string GetData() => "我是xiaososa";
}

到了这里我们就应该想到既然我们写了服务类和ViewModel类,那我们顺便去注入一下,打开ServiceLocator.cs

public class ServiceLocator 
{
    private IServiceProvider _serviceProvider;

    public ServiceLocator() 
    {
        var serviceCollection = new ServiceCollection();

        serviceCollection.AddSingleton<IDataAccess, DataAccess>();

        serviceCollection.AddSingleton<MainWindowViewModel>();

        _serviceProvider = serviceCollection.BuildServiceProvider();
    }
}

这样我们就可以让MainWindowViewModel去依赖IDataAccess,回到MainWindowViewModel类进行依赖注入,并填写一个方法让我们的名字改变

public partial class MainWindowViewModel : ObservableObject 
{
    private int _num = 1;

    [ObservableProperty]
    private string _personName = $"Person类与我无瓜";

    [ObservableProperty]
    private Person _person;

    [ObservableProperty]
    [NotifyCanExecuteChangedFor(nameof(UpdateCommand))]
    private bool _isEnabled;

    private readonly IDataAccess _dataAccess;

    public MainWindowViewModel(IDataAccess dataAccess) 
    {
        Person = new Person();
        _dataAccess = dataAccess;
    }

    

    [RelayCommand(CanExecute = nameof(CanButtonClick))]
    public async Task UpdateAsync() 
    {
        await Task.Delay(500);
        PersonName = $"{_dataAccess.GetData()}=>{_num}";

        Person = new Person() {
            Name = _dataAccess.GetData(),
            Id = ++_num
        };

    }

    private bool CanButtonClick() => IsEnabled;
}

按照我们的预想结果,在勾选IsEnabledde时候我们点击button以后0.5秒后名字会改变,并且id自增,请注意RelayCommand
等同于我们在构造函数和声明内容如下

public MainWindowViewModel(IDataAccess dataAccess) 
    {
        _dataAccess = dataAccess;
        UpdateAsyncRelayCommand = new AsyncRelayCommand(UpdateAsync);
    }

public AsyncRelayCommand UpdateAsyncRelayCommand { get; }
  1. 接下来我们简单的添加几个控件在MainWindow.xaml页面上
<Window x:Class="WpfMVVMIOCServiceLocator.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfMVVMIOCServiceLocator"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
        <Label Content="11111111111" FontSize="50" ></Label>
        <CheckBox Content="IsEnabled"  FontSize="30" HorizontalAlignment="Center"></CheckBox>
        <Label Content="Person类雨我无瓜" FontSize="50" HorizontalAlignment="Center" />
        <Button FontSize="25" Content="我是一个按钮" BorderBrush="Black" BorderThickness="2" Margin="10,10,10,10" HorizontalAlignment="Center" Command="{Binding ShowCommand}" />
    </StackPanel>
</Window>

效果如下
在这里插入图片描述

这时候我们就要运用ServicesLocator和MVVM模式,改变他们的显示
让我们先打开ServiceLocator.cs,因为之前我们已经注入过ViewModel了,现在我们声明公开一下MainWindowViewModel
代码如下,添加一个

public MainWindowViewModel? MainWindowViewModel => _serviceProvider.GetService();

private IServiceProvider _serviceProvider;

public MainWindowViewModel? MainWindowViewModel => _serviceProvider.GetService<MainWindowViewModel>();

public ServiceLocator() 
{
     var serviceCollection = new ServiceCollection();

     serviceCollection.AddSingleton<IDataAccess, DataAccess>();

     serviceCollection.AddSingleton<MainWindowViewModel>();

     _serviceProvider = serviceCollection.BuildServiceProvider();
 }

优雅的地方来了,这是我们能在Xaml文件下提示感知的关键,让我们先打开App.xaml,添加一下Resources

<Application x:Class="WpfMVVMIOCServiceLocator.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:local="clr-namespace:WpfMVVMIOCServiceLocator"
             StartupUri="MainWindow.xaml">
    <Application.Resources>
        <ResourceDictionary>
            <local:ServiceLocator x:Key="ServiceLocator"/>
        </ResourceDictionary>
    </Application.Resources>
</Application>

然后回到MainWindow.xaml,在Window处添加

DataContext=“{Binding MainWindowViewModel,Source={StaticResource ServiceLocator}}”

这里有一个优雅的写法,当你写到DataContext="{Binding的时候,先按一下空格,然后写一个逗号,在写Source={StaticResource ServiceLocator}}”,后面就可以自动感知到MainWindowViewModel

在这里插入图片描述

好了,这次我们就可以绑定值了,将刚才的控件都修改一下,mode我们选择双向绑定,意味着只要值有变化就会通知前台更改值

<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
    <Label Content="{Binding Person.Name,Mode=TwoWay}" FontSize="50" HorizontalAlignment="Center" />
    <CheckBox Content="IsEnabled" IsChecked="{Binding IsEnabled}" FontSize="30" HorizontalAlignment="Center"></CheckBox>
    <Label Content="{Binding PersonName,Mode=TwoWay}" FontSize="50" HorizontalAlignment="Center" />
    <Button FontSize="25" Content="{Binding Person.Id,Mode=TwoWay}" Width="200" BorderBrush="Black" BorderThickness="2" Margin="10,10,10,10" HorizontalAlignment="Center" Command="{Binding UpdateCommand}" />
</StackPanel>

然后让我们启动,这下我们的IOC_MVVM_ServiceLocator就建立好了,需要勾选IsEnabled才可以点击button
效果如下,button按钮的名字总是要比上面多1的,那我们的效果就达到了
在这里插入图片描述
好的,我们这次示例就到此为止了,
还有个很严重的问题,假如你们引入MVVM包和依赖注入包会报这个乱码错误蟹变露Exception.ToString0 奸姝楷板窜稿纺人覆?,不是你们的问题,是IDE的锅,建议重新安装组件或者升级,正常情况下是不冲突的
在这里插入图片描述
有小伙伴不明白的可以参考以下视频
MVVM:【用 CommunityToolkit.Mvvm 加速 MVVM 开发流程】https://www.bilibili.com/video/BV12x4y177qB?vd_source=b2be7496dbe636cb228643582ce2c4b3
依赖注入:【.NET 6 中 WPF 的依赖注入,包括工厂模式】https://www.bilibili.com/video/BV18V4y177TY?vd_source=b2be7496dbe636cb228643582ce2c4b3
ServiceLocator:【.NET MAUI HelloWorld Part 2】https://www.bilibili.com/video/BV1Gt4y177zA?vd_source=b2be7496dbe636cb228643582ce2c4b3

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/2514.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

网络技术与应用概论(上)——“计算机网络”

各位CSDN的uu们你们好呀&#xff0c;今天&#xff0c;小雅兰的内容依旧是计算机网络的一些知识点噢&#xff0c;下面&#xff0c;让我们进入计算机网络的世界吧 网络内涵 网络特征 网络定义 互联网发展过程 从ARPA网络到Internet 从低速互联网到高速互联网 从数据结构到统一网…

【C语言】通讯录的实现(静态版)

【C语言】通讯录的实现(静态版一.前言1.前期准备a.菜单实现b.联系人结构体的构建c.菜单选项的功能d.#define 的定义2.功能的实现a.初始化通讯录b.增加联系人c.显示通讯录d.查找联系人e.修改联系人d.删除联系人3. 总代码test.ccontact.ccontact.h一.前言 本文将会用c语言实现一…

Golang每日一练(leetDay0013)

目录 37. 解数独 Sudoku Solver &#x1f31f;&#x1f31f;&#x1f31f; 38. 外观数列 Count and Say &#x1f31f;&#x1f31f; 39. 组合总和 Combination Sum &#x1f31f;&#x1f31f; &#x1f31f; 每日一练刷题专栏 &#x1f31f; Golang每日一练 专栏 Py…

大数据技术之Hive

第1章Hive基本概念1.1 Hive1.1.1 Hive的产生背景在那一年的大数据开源社区&#xff0c;我们有了HDFS来存储海量数据、MapReduce来对海量数据进行分布式并行计算、Yarn来实现资源管理和作业调度。但是面对海量数据和负责的业务逻辑&#xff0c;开发人员要编写MR来对数据进行统计…

【Go】K8s 管理系统项目[Jenkins Pipeline K8s环境–应用部署]

K8s 管理系统项目[Jenkins Pipeline K8s环境–应用部署] 1. k8s-plantform-api-Pipeline 考虑到实际工作中前后端可能是不同的同学完成,一般Api部分完成后改动会比较小,web部分改动会比较频繁.于是将api和web分了2个pipeline实现 1.1 GIt仓库 docker目录存放镜像构建相关文件…

简介虚拟地址空间:保障进程间独立性的机制

我们知道&#xff0c;进程之间是相互独立的&#xff0c;在操作系统级别中&#xff0c;一个进程所执行的程序无法直接访问另一个进程所执行的内存区域&#xff08;即实现进程间通信比较困难&#xff09;&#xff1b;一个进程运行的失败也不会影响其它进程的运行。这使我们的操作…

vue编程方法

1&#xff0c;app.vue 其中的moundted只是被执行一次。 系统中所有的组件都放到app。vue文件中。放到根组件中的只是被执行一次的代码可以放到main.js中码&#xff1f; 不可以&#xff0c;因为main文件只是一个js文件不是一个组件。组件中的一些属性不能被使用。比如&#xff…

VS Code上搭建Vue开发环境超详细教程

这篇关于在Visual Studio Code上搭建vue开发环境的超详细教程手把手教会你! 首先在Visual Studio Code上搭建vue开发环境有几个步骤&#xff1a; 1、下载安装node.js 2、安装npm 3、安装cnpm 4、安装vue/cli脚手架 5、创建vue项目 6、运行vue项目 1.下载安装node.js 地址&…

鸟哥的Linux私房菜 正则表示法与文件格式化处理

第十一章、正则表示法与文件格式化处理 https://linux.vbird.org/linux_basic/centos7/0330regularex.php 简体版 http://cn.linux.vbird.org/linux_basic/0330regularex.php 11.2.2 grep的一些高级选项 例题一、搜索特定字符串 例题二、利用中括号 [] 来搜寻集合字符 例题四…

8个python自动化脚本提高打工人幸福感~比心~

人生苦短&#xff0c;我用Python 最近有许多打工人都找我说打工好难 每天都是执行许多重复的任务&#xff0c; 例如阅读新闻、发邮件、查看天气、打开书签、清理文件夹等等&#xff0c; 使用自动化脚本&#xff0c;就无需手动一次又一次地完成这些任务&#xff0c; 非常方便…

蓝桥杯嵌入式RTC实时时钟

文章目录 前言一、RTC是什么二、cubemx的配置三、函数的使用总结前言 本篇文章将给大家介绍RTC实时时钟。 一、RTC是什么 STM32的实时时钟RTC是一个独立的定时器,RTC时钟内部依靠BCD码计数。RTC实时时钟提高时钟、闹钟、日历功能。RTC功耗较低,可以使用在低功耗设备上。 …

Redis为什么选择单线程?Redis为什么这么快?

目录专栏导读一、Redis版本迭代二、Redis4.0之前为什么一直采用单线程&#xff1f;三、Redis6.0引入多线程四、Redis主线程和IO线程是如何完成请求的&#xff1f;1、服务端和客户端建立socket连接2、IO线程读取并解析请求3、主线程执行请求命令4、IO线程会写回socket和主线程清…

DM8:LINUX环境安装DM8数据库安装条件--GLIBC版本要求

DM8&#xff1a;LINUX环境安装DM8数据库安装条件--GLIBC版本要求环境介绍1 检查 GLIBC 版本号2 /tmp 临时目录空间要等于或大于2GB3 报错截图3.1 导入授权报错3.2 设置时区报错3.3 DmAPService启动失败3.4 初始化实例报错4 更多达梦数据库使用经验环境介绍 在LINUX环境安装达梦…

一线大厂软件测试常见面试题1500问,背完直接拿捏面试官,

三、测试理论 3.1 你们原来项目的测试流程是怎么样的? 我们的测试流程主要有三个阶段&#xff1a;需求了解分析、测试准备、测试执行。 1、需求了解分析阶段 我们的SE会把需求文档给我们自己先去了解一到两天这样&#xff0c;之后我们会有一个需求澄清会议&#xff0c; 我…

基于Springboot实现口腔牙诊所网站平台【源码+论文】

基于Springboot实现口腔牙诊所网站平台【源码论文】开发语言&#xff1a;Java 框架&#xff1a;springboot JDK版本&#xff1a;JDK1.8 服务器&#xff1a;tomcat7 数据库&#xff1a;mysql 5.7 数据库工具&#xff1a;Navicat11 开发软件&#xff1a;eclipse/myeclipse/idea M…

整合SpringCache

整合SpringCache 1、引入依赖cache还有redis <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-cache</artifactId> </dependency>2、写配置 spring:cache:type: redis3、测试使用缓存 Cache…

大数据现在找工作难么

大数据行业工作好找还是难找不是光靠嘴说出来的结合实际&#xff0c;看看市场上的招聘需求和岗位要求就大致知道了 要想符合企业用人规范&#xff0c;学历&#xff0c;工作经验&#xff0c;掌握技能都是非常重要的~ 先来看几个招聘网站的报告数据&#xff1a; Boss直聘发布的…

【蓝桥杯】 C++ 数字三角形 动态规划 ⭐⭐

文章目录题目描述输入描述输出描述实现代码解题思路注意点知识点题目描述 上图给出了一个数字三角形。从三角形的顶部到底部有很多条不同的路径。对于每条路径&#xff0c;把路径上面的数加起来可以得到一个和&#xff0c;你的任务就是找到最大的和&#xff08;路径上的每一步…

Python嵌套函数(Nested function)和闭包(closure)

Python嵌套函数&#xff08;Nested function&#xff09;和闭包&#xff08;closure&#xff09; 闭包&#xff08;closure&#xff09;是建立在嵌套函数基础上的&#xff0c;是一种特殊的嵌套函数结构。 先看嵌套函数&#xff08;Nested function&#xff09;。 Python允许…

gan实战(DCGAN、)

一、DCGAN 1.1 参数 &#xff08;1&#xff09;输入&#xff1a;会被放缩到6464 &#xff08;2&#xff09;输出&#xff1a;6464 &#xff08;3&#xff09;数据集&#xff1a; 1.2 实现 import glob import torch from PIL import Image from torch import nn from torch.u…