在WPF中使用依赖注入的方式创建视图

时间:2022-09-03 08:00:36

在WPF中使用依赖注入的方式创建视图

0x00 问题的产生

互联网时代桌面开发真是越来越少了,很多应用都转到了浏览器端和移动智能终端,相应的软件开发上的新技术应用到桌面开发的文章也很少。我之前主要做WPF,今年开始学习Web应用开发,于是就接触到了.NET Core,其中的很多概念很值得在桌面开发中借鉴。例如在.NET Core MVC中,Controller的依赖是通过构造函数注入的,注入的过程由框架实现,我们在写Controller时只要在构造函数参数中罗列出要依赖的服务即可,进一步的,把服务抽象为接口,那么核心的业务逻辑就彻底解耦出来了,依赖的服务可以是任意的实现方式(当然前提是要满足需求)。WPF一般都是用MVVM模式开发,那么是不是可以让ViewModel对其它服务的依赖也通过构造函数自动注入,而不是每次都要new出一个ViewModel呢?这篇文章主要就讨论这个问题,并尝试写了个View和ViewModel的容器来实现。

0x01 最初的设计

.NET Core MVC中之所以能做到Controller的依赖自动注入,主要就是因为Controller实例是由MVC框架创建的。我们要想让ViewModel中的依赖自动注入,那么这个ViewModel肯定需要自动创建。考虑到View与ViewModel之间的对应也算是一种依赖关系,那么就可以把View和ViewModel之间的这种对应关系以及其它服务的依赖关系都放到容器里,当需要View的时候,根据View的类型从容器中找到对应的ViewModel,然后根据ViewModel的依赖,从容器中获取服务,然后把View的DataContext设置为ViewModel的实例,最终返回View,那么就实现了ViewModel的自动依赖注入了。

在WPF中使用依赖注入的方式创建视图

0x02 更进一步的设计

按照上面那个方案我写了一个简易的依赖注入容器,证明是可以用的。不过要想真正在相对严肃一点的环境中开发,对依赖注入容器的要求就不是那么简单了。我需要花时间去开发一个严谨一点的依赖注入容器,这不仅需要时间,关键水平有限,目前市面上已经存在了很多优秀的依赖注入容器,我没必要造*(为了学习或更深入理解原理而去造*的行为不在此列),但常见的依赖注入容器在配置服务时(例如绑定A和B)一般都限制B对A有继承关系,所以现有的依赖注入容器无法配置View和ViewModel的依赖。因此考虑把View和ViewModel的依赖关系单独存到一个容器中,服务的依赖放到第三方容器,为了能够适配第三方容器,可以提供一个接口,通过接口对第三方容器进行简易的包装即可使用,这样就可以任意选择自己喜欢的强大的第三方依赖注入容器了。

在WPF中使用依赖注入的方式创建视图

0x03 部分代码和示例

在开始看代码之前,先说一下存储View和ViewModel关系的容器AvalonContainer(后面简称View容器),使用这个容器的Wire方法可以配置View和ViewModel之间的对应关系,GetView方法可以获取View,同时给View的DataContext配置好了指定的ViewModel,并且ViewModel注入了依赖。要创建一个AvalonContainer需要在构造函数中传入IContainer对象,这个接口用于对第三方依赖注入容器实现包装,以便用于AvalonContainer,第三方依赖注入容器主要作用是从中获取ViewModel的依赖,以及往容器中添加ViewModel(如果需要的话)。

我自己写的依赖注入容器太简易了,当时只是用来测试,实际应用中应该都会使用第三方容器,所以示例直接用的第三方容器Ninject。

核心的步骤是创建一个Ninject容器,用Ninject容器绑定依赖,然后用Ninject容器创建View容器,配置View和ViewModel依赖。这样需要时就可以直接从View容器创建View,获得的View的DataContext已经设置为ViewModel实例并注入了ViewModel的依赖。

在WPF中使用依赖注入的方式创建视图

ViewModel中一般在构造函数参数中注入依赖。对于不同的依赖注入容器,也可以通过给属性配置相应的Attribute的方式声明依赖注入,不过这种方式对ViewModel的侵入太强了,而且不同的依赖注入容器往往提供不同的Attribute,更换时会比较麻烦,还是构造函数注入比较好,更换依赖注入容器不会产生影响。下面截图是TestOneView对应的ViewModel,在构造函数中注入了仓储和日志的依赖,感觉就像.NET Core MVC中的Controller。

在WPF中使用依赖注入的方式创建视图

当需要OneTestView窗口时,可以如下图所示创建并显示。

在WPF中使用依赖注入的方式创建视图

为了能够适配任意的第三方依赖注入容器,提供了IContainer接口,在使用第三方依赖注入容器时需要通过这个接口适配一下,这种感觉就像电脑输出接口可以有HDIM、DVI、VGA,显示器输入接口只有VGA,需要转接头来转换一下。

在WPF中使用依赖注入的方式创建视图

其中Get方法用于从第三方容器中获取ViewModel并注入依赖,Wire<T>()方法用于往第三方容器中添加ViewModel。其中token是针对自带依赖注入容器的,完全可以忽略不管。

其实对于Ninject来说是完全不需要Wire这个方法的,因为即使这个类型没有添加到容器中,在Get时Ninject也会创建对象并注入其中的依赖,所以对Ninject的包装如下,Wire方法直接忽略即可。但不能保证所有的第三方依赖注入容器都有这个特性,所以还是保留了这个接口。

在WPF中使用依赖注入的方式创建视图

这样依赖注入容器和View容器通过IContainer解耦,更换依赖注入容器不会影响到业务逻辑。

如果因为某些特殊原因需要给同一个View绑定不同的ViewModel,可以在Wire时提供token参数,在GetView时使用同样的token参数即可获取相应的ViewModel。

0x04 写在最后

View容器写好后自己用了下感觉还可以,但因为ViewModel是动态添加的,所以无法在设计时看到数据,这确实是个问题。另外要说下起名字真的很难,之前大多数都是出于学习/练习的目的,就直接加个Ayx前缀,不过这次想发布一下,考虑到WPF开发代号是Avalon,就把它叫了AvalonDI。最后关于配置View和ViewModel依赖的方法,在NInject中是用的Bind,这个感觉比较好理解。不过我觉得把接口和接口的实现绑定到一起,用装配/组装更贴切。想像一下,电视提供了标准输入接口,我们可以接录像机、游戏机、电脑。同样游戏机提供了接口,可以插不同的卡带、不同的手柄,当把他们连在一起时,用Wire感觉更合适一点。

Github:https://github.com/durow/AvalonDI

nuget:Install-Package Ayx.AvalonDI

samples里面提供了一个WpfSample,用的自带的依赖注入容器,一个NinjectSample,用的Ninject作为依赖注入容器。

在WPF中使用依赖注入的方式创建视图的更多相关文章

  1. dotnetcore3&period;1 WPF 中使用依赖注入

    dotnetcore3.1 WPF 中使用依赖注入 Intro 在 ASP.NET Core 中默认就已经集成了依赖注入,最近把 DbTool 迁移到了 WPF dotnetcore 3.1, 在 W ...

  2. Spring学习(三)——Spring中的依赖注入的方式

    [前面的话] Spring对我太重要了,做个关于web相关的项目都要使用Spring,每次去看Spring相关的知识,总是感觉一知半解,没有很好的系统去学习一下,现在抽点时间学习一下Spring.不知 ...

  3. JavaEE开发之Spring中的依赖注入与AOP

    上篇博客我们系统的聊了<JavaEE开发之基于Eclipse的环境搭建以及Maven Web App的创建>,并在之前的博客中我们聊了依赖注入的相关东西,并且使用Objective-C的R ...

  4. &period;NET CORE——Console中使用依赖注入

    我们都知道,在 ASP.NET CORE 中通过依赖注入的方式来使用服务十分的简单,而在 Console 中,其实也只是稍微绕了个小弯子而已.不管是内置 DI 组件或者第三方的 DI 组件(如Auto ...

  5. JavaEE开发之Spring中的依赖注入与AOP编程

    上篇博客我们系统的聊了<JavaEE开发之基于Eclipse的环境搭建以及Maven Web App的创建>,并在之前的博客中我们聊了依赖注入的相关东西,并且使用Objective-C的R ...

  6. Asp&period;net core中的依赖注入

    使用服务 在Asp.net core的Controller中,可以通过如下两种方式获取系统注入的服务: 构造函数 可以直接在构造函数中传入所依赖的服务,这是非常常见的DI注入方式. public Va ...

  7. MVVM模式解析和在WPF中的实现(六) 用依赖注入的方式配置ViewModel并注册消息

    MVVM模式解析和在WPF中的实现(六) 用依赖注入的方式配置ViewModel并注册消息 系列目录: MVVM模式解析和在WPF中的实现(一)MVVM模式简介 MVVM模式解析和在WPF中的实现(二 ...

  8. Spring依赖注入的方式、类型、Bean的作用域、自动注入、在Spring配置文件中引入属性文件

    1.Spring依赖注入的方式 通过set方法完成依赖注入 通过构造方法完成依赖注入 2.依赖注入的类型 基本数据类型和字符串 使用value属性 如果是指向另一个对象的引入 使用ref属性 User ...

  9. Webservice WCF WebApi 前端数据可视化 前端数据可视化 C&num; asp&period;net PhoneGap html5 C&num; Where 网站分布式开发简介 EntityFramework Core依赖注入上下文方式不同造成内存泄漏了解一下? SQL Server之深入理解STUFF 你必须知道的EntityFramework 6&period;x和EntityFramework Cor

    Webservice WCF WebApi   注明:改编加组合 在.net平台下,有大量的技术让你创建一个HTTP服务,像Web Service,WCF,现在又出了Web API.在.net平台下, ...

随机推荐

  1. nodeJS Express 删除 x-powered-by

    在使用Express4 Header头部会输出,在晚上搜索几种方案也没有产生效果,就看了一下官方文档 Property Type               Value Default     x-p ...

  2. 不显示UITableView底部多余的分割线

    self.tableView.tableFooterView = [[UIView alloc] initWithFrame:CGRectZero];

  3. Python列表和字典的方法总结

    列表方法: 方法 说明 append( item ) 在列表末尾插入(item ) count( element ) 返回element在列表中出现的次数 extend( newlist ) 将new ...

  4. javascript中处理引号编码&amp&semi;&num;034&semi;

    1. <c:out value='${jxOrgJsonStr}' escapeXml="false"/> 2.或者使用innerText 直接接受${jxOrgJso ...

  5. Cat 跨线程之 TaggedTransaction 用法和原理分析

    代码 package com.dianping.cat.message.internal; import com.dianping.cat.Cat; import com.dianping.cat.m ...

  6. dojo省份地市级联之省份Dao实现类(五)

    dojo省份地市级联之省份Dao实现类 ProvinceDaoImpl.java: /** * */ package com.you.dao.impl; import java.util.ArrayL ...

  7. 攻防组网之—-MikroTik软路由的配置和FUZZ

    作者:红发香克斯 开篇之前一直想借助一台高性能PC利用VMWare组建一个攻防网络,之前有一贴写过找到了一个软路由系统,就是可以在VMWare中使用的路由器操作系统,那个系统还挺常用,上次帖子写到了安 ...

  8. python&colon; 基本知识记录

    1.图像输入输出操作 scikit-image: 图像输入输出库: 2.pyqt5库的安装: 对于python2.x, 使用pip install python-qt5即可以自动安装: 3.文件编码: ...

  9. Python集合&lpar;set&rpar;

    Python中的集合同数学中的集合概念类似,也是用于保存不重复的元素.他有可变集合(set),和不可变集合(frozenset);可变集合(set)是无序的可变的. 创建集合 直接使用{}创建 set ...

  10. Java 8新特性之 并行和并行数组(八恶人-8)

    Jody Domingre 多莫歌·乔迪 “How you doing, dummy?” 你还好吗,傻瓜 一.基本介绍 Java8不仅增加了Stream,而且还增加了parallelStream(并行 ...