Prism 原始碼解讀4-ViewModel注入
阿新 • • 發佈:2020-04-02
## 介紹
介紹一個Prism的MVVM實現,主要介紹Prism如何在WPF上進行的一些封裝,以實現MVVM。MVVM到底是什麼呢?看一下這一幅經典的圖
![](https://img2020.cnblogs.com/blog/1078802/202004/1078802-20200401223814536-572067637.png)
以前沒有ViewModel這個概念,就是將Model傳遞到View顯示,這樣軟體也可以工作,但卻很混亂,一旦VIew要改動,一點點的改動都會造成很多程式碼需要改動,不利於維護。再者VIew層充斥著各種解析Model的程式碼,這些程式碼完全不屬於View啊。平白無故的給View增加了很多職責。這是壞程式碼的味道。所以就有了ViewModel。ViewModel負責幹什麼,必須要幹什麼,其實ViewModel的職責就是將自己的資料繫結到View顯示,同時資料變化需要通知View,View上客戶的操作及時響應,至於資料怎麼解析,從哪裡獲取,View的響應都應該方法後一層,可以是Controller,可以是Servicer,可以是Presenter。也就是業務邏輯儘量推到後一層。
試想一下,系統裡的Model有很多,有資料庫對應的資料庫模型,有業務於對應的領域模型,有用於資料互動的DTO也是模型,那麼對應的View有一個ViewModel也不覺得奇怪。
## 0 ViewModel定位
MVVM的第一步就是要解決ViewModel的依賴注入問題,框架如何不著痕跡的將View對應的VIewModel注入到依賴屬性DataContext。
還記得PrismApplicationBase類嗎,就是繼承Application,將整個Prism框架元件注入到Unity的那個類,
![1585748254700](https://img2020.cnblogs.com/blog/1078802/202004/1078802-20200401223758742-1379739242.png)
![1585748299765](https://img2020.cnblogs.com/blog/1078802/202004/1078802-20200401223758743-1798223979.png)
![1585748279636](https://img2020.cnblogs.com/blog/1078802/202004/1078802-20200401223758905-1947263339.png)
看到第一步是啥?ConfigureViewModelLocator,配置ViewModelLocator,急人之所急,Prism框架的第一步配置ViewModelLocator,
![1585748453172](https://img2020.cnblogs.com/blog/1078802/202004/1078802-20200401223758905-1784978101.png)
好吧,第一步就是設定ViewModelFactory,這個工廠就是通過View的型別和例項從Unity容器中獲取ViewModel例項。
![1585748824956](https://img2020.cnblogs.com/blog/1078802/202004/1078802-20200401223758905-108722794.png)
![1585748835165](https://img2020.cnblogs.com/blog/1078802/202004/1078802-20200401223758906-1439285613.png)
噢!這個View引數還沒用上。
再來看看這個包含ViewModelFactory的ViewModelLocationProvider。
![1585748957518](https://img2020.cnblogs.com/blog/1078802/202004/1078802-20200401223758906-1493981916.png)
![1585749152851](https://img2020.cnblogs.com/blog/1078802/202004/1078802-20200401223758908-1025201669.png)
從這個名字我們可以大膽猜測,這個類應該是負責真正解析ViewModel的位置的,看到這個類的方法,有ViewModelFactory,有Register,有GetViewModelByXXX。
![1585749500406](https://img2020.cnblogs.com/blog/1078802/202004/1078802-20200401223758904-24359347.png)
這個類中一個委託欄位_defaultViewTypeToViewModelTypeResolver,從這個欄位我們可以看出是預設VIewModel解析方式,可以看出就是把View完整型別名中的Views替換成ViewModels,然後返回Type,從這裡面我們知道View的名字一定要含有Views,ViewModel一定要含有ViewModels。
![1585751286821](https://img2020.cnblogs.com/blog/1078802/202004/1078802-20200401223758906-1486319483.png)
好吧,知道了哪裡解析的再來看看哪裡呼叫的。
![1585749866991](https://img2020.cnblogs.com/blog/1078802/202004/1078802-20200401223758907-213170596.png)
prism:ViewModelLocator.AutoWireViewModel="True",看到了,將ViewModelLocator的依賴屬性AutoWireViewModel至為True,可以進一步推測ViewModelLocator裡面肯定呼叫了ViewModelLocationProvider的相關方法以獲得ViewModel的型別或例項。
![1585750033315](https://img2020.cnblogs.com/blog/1078802/202004/1078802-20200401223758895-402136856.png)
![1585750054551](https://img2020.cnblogs.com/blog/1078802/202004/1078802-20200401223758908-163048190.png)
![1585750076882](https://img2020.cnblogs.com/blog/1078802/202004/1078802-20200401223758908-375619841.png)
依賴屬性改變觸發了AutoWireViewModelChanged方法,然後呼叫ViewModelLocationProvider.AutoWireViewModelChanged
![1585750149579](https://img2020.cnblogs.com/blog/1078802/202004/1078802-20200401223758909-1341108795.png)
![1585750194664](https://img2020.cnblogs.com/blog/1078802/202004/1078802-20200401223758910-2142519225.png)
![1585750209281](https://img2020.cnblogs.com/blog/1078802/202004/1078802-20200401223758909-373258121.png)
![1585750223719](https://img2020.cnblogs.com/blog/1078802/202004/1078802-20200401223758909-448020299.png)
先去檢視兩個字典,一個字典key是View是例項,另一個字典key是View的Type,都沒有呼叫,然後呼叫ViewModelLocationProvider.**_defaultViewTypeToViewModelTypeResolver**,也就是預設解析,在這邊解析獲得VIewModel的型別,然後通過預設工廠獲得ViewModel例項。並繫結到VIew的DataContext。
至此,知道了整個預設VIewModel解析的全部過程,梳理一下
- 在程式開始向ViewModelLocationProvider中設定ViewModel型別工廠,也就是Unity。
- ViewModelLocationProvider就是ViewModel獲取的地方有兩個字典都應該是存放viewmodel,有一個預設解析是通過View的type解析出ViewModel的type。
- 在Xaml中通過ViewModelLocator的依賴屬性AutoWireViewModel呼叫ViewModelLocationProvider的AutoWireViewModelChanged來實現繫結。
## 1 自定義ViewModel定位
通過0的介紹,想一下怎麼自定義實現VIewModel定位,有幾種方法,
1. 提前向ViewModelLocationProvider的字典中新增ViewModel的型別
2. 改變_defaultViewTypeToViewModelTypeResolver解析方式
3. 修改工廠。這個不能從根本上改變。
這個例子用的是第二種。
![1585751155024](https://img2020.cnblogs.com/blog/1078802/202004/1078802-20200401223758910-30059270.png)
在程式的開始重寫ConfigureViewModelLocator方法,除了向ViewModelLocationProvider中新增ViewModelFactory外,還修改了_defaultViewTypeToViewModelTypeResolver解析方式。直接就通過View的type後面家長ViewModel,簡單粗暴。
![1585751325000](https://img2020.cnblogs.com/blog/1078802/202004/1078802-20200401223758904-1726849390.png)
## 2 自定義ViewModel解析
這種方法就是上面提到的1方法
- 提前向ViewModelLocationProvider的字典中新增ViewModel的型別
![1585751470206](https://img2020.cnblogs.com/blog/1078802/202004/1078802-20200401223758910-961587219.png)
這張方法顯然有很大的弊端,當程式中有很多View時怎麼能手動新增呢,只能適用與特殊的View和ViewModel的解析,如Shell的VIewModel的解析。
這種解析方法也不用在意View和ViewModel的名字了。
## 總結
從ViewModel的解析中,我們看到一種設計模式,View依賴ViewModelLocator,ViewModelLocator依賴ViewModelLocationProvider,ViewModelLocationProvider負責具體解析出對應的例項,相當於ViewModelRegistry,其中當然以有對工廠的