在WPF中使用依赖注入的方式创建视图
在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的自动依赖注入了。
0x02 更进一步的设计
按照上面那个方案我写了一个简易的依赖注入容器,证明是可以用的。不过要想真正在相对严肃一点的环境中开发,对依赖注入容器的要求就不是那么简单了。我需要花时间去开发一个严谨一点的依赖注入容器,这不仅需要时间,关键水平有限,目前市面上已经存在了很多优秀的依赖注入容器,我没必要造轮子(为了学习或更深入理解原理而去造轮子的行为不在此列),但常见的依赖注入容器在配置服务时(例如绑定A和B)一般都限制B对A有继承关系,所以现有的依赖注入容器无法配置View和ViewModel的依赖。因此考虑把View和ViewModel的依赖关系单独存到一个容器中,服务的依赖放到第三方容器,为了能够适配第三方容器,可以提供一个接口,通过接口对第三方容器进行简易的包装即可使用,这样就可以任意选择自己喜欢的强大的第三方依赖注入容器了。
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的依赖。
ViewModel中一般在构造函数参数中注入依赖。对于不同的依赖注入容器,也可以通过给属性配置相应的Attribute的方式声明依赖注入,不过这种方式对ViewModel的侵入太强了,而且不同的依赖注入容器往往提供不同的Attribute,更换时会比较麻烦,还是构造函数注入比较好,更换依赖注入容器不会产生影响。下面截图是TestOneView对应的ViewModel,在构造函数中注入了仓储和日志的依赖,感觉就像.NET Core MVC中的Controller。
当需要OneTestView窗口时,可以如下图所示创建并显示。
为了能够适配任意的第三方依赖注入容器,提供了IContainer接口,在使用第三方依赖注入容器时需要通过这个接口适配一下,这种感觉就像电脑输出接口可以有HDIM、DVI、VGA,显示器输入接口只有VGA,需要转接头来转换一下。
其中Get方法用于从第三方容器中获取ViewModel并注入依赖,Wire<T>()方法用于往第三方容器中添加ViewModel。其中token是针对自带依赖注入容器的,完全可以忽略不管。
其实对于Ninject来说是完全不需要Wire这个方法的,因为即使这个类型没有添加到容器中,在Get时Ninject也会创建对象并注入其中的依赖,所以对Ninject的包装如下,Wire方法直接忽略即可。但不能保证所有的第三方依赖注入容器都有这个特性,所以还是保留了这个接口。
这样依赖注入容器和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中使用依赖注入的方式创建视图的更多相关文章
- dotnetcore3.1 WPF 中使用依赖注入
dotnetcore3.1 WPF 中使用依赖注入 Intro 在 ASP.NET Core 中默认就已经集成了依赖注入,最近把 DbTool 迁移到了 WPF dotnetcore 3.1, 在 W ...
- Spring学习(三)——Spring中的依赖注入的方式
[前面的话] Spring对我太重要了,做个关于web相关的项目都要使用Spring,每次去看Spring相关的知识,总是感觉一知半解,没有很好的系统去学习一下,现在抽点时间学习一下Spring.不知 ...
- JavaEE开发之Spring中的依赖注入与AOP
上篇博客我们系统的聊了<JavaEE开发之基于Eclipse的环境搭建以及Maven Web App的创建>,并在之前的博客中我们聊了依赖注入的相关东西,并且使用Objective-C的R ...
- .NET CORE——Console中使用依赖注入
我们都知道,在 ASP.NET CORE 中通过依赖注入的方式来使用服务十分的简单,而在 Console 中,其实也只是稍微绕了个小弯子而已.不管是内置 DI 组件或者第三方的 DI 组件(如Auto ...
- JavaEE开发之Spring中的依赖注入与AOP编程
上篇博客我们系统的聊了<JavaEE开发之基于Eclipse的环境搭建以及Maven Web App的创建>,并在之前的博客中我们聊了依赖注入的相关东西,并且使用Objective-C的R ...
- Asp.net core中的依赖注入
使用服务 在Asp.net core的Controller中,可以通过如下两种方式获取系统注入的服务: 构造函数 可以直接在构造函数中传入所依赖的服务,这是非常常见的DI注入方式. public Va ...
- MVVM模式解析和在WPF中的实现(六) 用依赖注入的方式配置ViewModel并注册消息
MVVM模式解析和在WPF中的实现(六) 用依赖注入的方式配置ViewModel并注册消息 系列目录: MVVM模式解析和在WPF中的实现(一)MVVM模式简介 MVVM模式解析和在WPF中的实现(二 ...
- Spring依赖注入的方式、类型、Bean的作用域、自动注入、在Spring配置文件中引入属性文件
1.Spring依赖注入的方式 通过set方法完成依赖注入 通过构造方法完成依赖注入 2.依赖注入的类型 基本数据类型和字符串 使用value属性 如果是指向另一个对象的引入 使用ref属性 User ...
- Webservice WCF WebApi 前端数据可视化 前端数据可视化 C# asp.net PhoneGap html5 C# Where 网站分布式开发简介 EntityFramework Core依赖注入上下文方式不同造成内存泄漏了解一下? SQL Server之深入理解STUFF 你必须知道的EntityFramework 6.x和EntityFramework Cor
Webservice WCF WebApi 注明:改编加组合 在.net平台下,有大量的技术让你创建一个HTTP服务,像Web Service,WCF,现在又出了Web API.在.net平台下, ...
随机推荐
- 理解加密算法(三)——创建CA机构,签发证书并开始TLS通信
接理解加密算法(一)--加密算法分类.理解加密算法(二)--TLS/SSL 1 不安全的TCP通信 普通的TCP通信数据是明文传输的,所以存在数据泄露和被篡改的风险,我们可以写一段测试代码试验一下. ...
- 踩石行动:ViewPager无限轮播的坑
2016-6-19 前言 View轮播效果在app中很常见,一想到左右滑动的效果就很容易想到使用ViewPager来实现.对于像我们常说的banner这样的效果,具备无限滑动的功能是可以用ViewPa ...
- Visaul Studio 常用快捷键的动画演示
从本篇文章开始,我将会陆续介绍提高 VS 开发效率的文章,欢迎大家补充~ 在进行代码开发的时候,我们往往会频繁的使用键盘.鼠标进行协作,但是切换使用两种工具会影响到我们的开发速度,如果所有的操作都可以 ...
- JavaScript之链式结构序列化
一.概述 在JavaScript中,链式模式代码,太多太多,如下: if_else: if(...){ //TODO }else if(...){ //TODO }else{ //TODO } swi ...
- 浏览器中用JavaScript获取剪切板中的文件
本文转自我的个人网站 , 原文地址:http://www.zoucz.com/blog/2016/01/29/get-file-from-clipboard/ ,欢迎前往交流讨论 在网页上编辑内容 ...
- UWP开发之Mvvmlight实践七:如何查找设备(Mobile模拟器、实体手机、PC)中应用的Log等文件
在开发中或者后期测试乃至最后交付使用的时候,如果应用出问题了我们一般的做法就是查看Log文件.上章也提到了查看Log文件,这章重点讲解下如何查看Log文件?如何找到我们需要的Packages安装包目录 ...
- Tomcat启动报错org.springframework.web.context.ContextLoaderListener类配置错误——SHH框架
SHH框架工程,Tomcat启动报错org.springframework.web.context.ContextLoaderListener类配置错误 1.查看配置文件web.xml中是否配置.or ...
- 分享两个BPM配置小技巧
1.小技巧 流程图修改后发布的话版本号会+1,修改次数多了之后可能会导致版本号很高,这个时候可以将流程导出,然后删除对应的流程包再导入,发布数据模型和流程图之后,版本清零 2.小技巧 有的同事入职后使 ...
- Visual Studio 2013 添加一般应用程序(.ashx)文件到SharePoint项目
默认,在用vs2013开发SharePoint项目时,vs没有提供一般应用程序(.ashx)的项目模板,本文解决此问题. 以管理员身份启动vs2013,创建一个"SharePoint 201 ...
- photoshop:无法完成请求 因为暂存盘已满
今天photoshop打开一个问题,提醒:无法完成请求因为暂存盘已满 不用担心这个问题很好解决可能是你做的图比较大并不需要清理C盘空间 选择:编辑→首选项→暂存盘 设置第一暂存盘为D盘或E盘 总之 第 ...