1. 程式人生 > >Prism 原始碼解讀4-ViewModel注入

Prism 原始碼解讀4-ViewModel注入

## 介紹 介紹一個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,其中當然以有對工廠的