Prism 源碼解讀4-ViewModel注入

介紹

介紹一個Prism的MVVM實現,主要介紹Prism如何在WPF上進行的一些封裝,以實現MVVM。MVVM到底是什麼呢?看一下這一幅經典的圖

以前沒有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

1585748299765

1585748279636

看到第一步是啥?ConfigureViewModelLocator,配置ViewModelLocator,急人之所急,Prism框架的第一步配置ViewModelLocator,

1585748453172

好吧,第一步就是設置ViewModelFactory,這個工廠就是通過View的類型和實例從Unity容器中獲取ViewModel實例。

1585748824956

1585748835165

噢!這個View參數還沒用上。

再來看看這個包含ViewModelFactory的ViewModelLocationProvider。

1585748957518

1585749152851

從這個名字我們可以大膽猜測,這個類應該是負責真正解析ViewModel的位置的,看到這個類的方法,有ViewModelFactory,有Register,有GetViewModelByXXX。

1585749500406

這個類中一個委託欄位_defaultViewTypeToViewModelTypeResolver,從這個欄位我們可以看出是默認VIewModel解析方式,可以看出就是把View完整類型名中的Views替換成ViewModels,然後返回Type,從這裡面我們知道View的名字一定要含有Views,ViewModel一定要含有ViewModels。

1585751286821

好吧,知道了哪裡解析的再來看看哪裡調用的。

1585749866991

prism:ViewModelLocator.AutoWireViewModel="True",看到了,將ViewModelLocator的依賴屬性AutoWireViewModel至為True,可以進一步推測ViewModelLocator裡面肯定調用了ViewModelLocationProvider的相關方法以獲得ViewModel的類型或實例。

1585750033315

1585750054551

1585750076882

依賴屬性改變觸發了AutoWireViewModelChanged方法,然後調用ViewModelLocationProvider.AutoWireViewModelChanged

1585750149579

1585750194664

1585750209281

1585750223719

先去查看兩個字典,一個字典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

在程式的開始重寫ConfigureViewModelLocator方法,除了向ViewModelLocationProvider中添加ViewModelFactory外,還修改了_defaultViewTypeToViewModelTypeResolver解析方式。直接就通過View的type後面家長ViewModel,簡單粗暴。

1585751325000

2 自定義ViewModel解析

這種方法就是上面提到的1方法

  • 提前向ViewModelLocationProvider的字典中添加ViewModel的類型

1585751470206

這張方法顯然有很大的弊端,當程式中有很多View時怎麼能手動添加呢,只能適用與特殊的View和ViewModel的解析,如Shell的VIewModel的解析。

這種解析方法也不用在意View和ViewModel的名字了。

總結

從ViewModel的解析中,我們看到一種設計模式,View依賴ViewModelLocator,ViewModelLocator依賴ViewModelLocationProvider,ViewModelLocationProvider負責具體解析出對應的實例,相當於ViewModelRegistry,其中當然以有對工廠的依賴。