MVVM框架從WPF移植到UWP遇到的問題和解決方法
0x00 起因
這幾天開始學習UWP了,之前有WPF經驗,所以總體感覺還可以,看了一些基礎概念和主題,寫了幾個測試程序,突然想起來了前一段時間在WPF下寫的簡易的MVVM框架(MVVM模式和在WPF中的實現),都是.NET平台的,移到通用類庫里只要復制粘貼就可以了吧。抱着這個心態試了下,結果代碼一片紅,折騰了一下午總算搞得差不多了,第二天寫了個測試試了下感覺基本問題應該是解決了。現在總結一下自己踩到的坑。
0x01 命令綁定
之前在WPF中實現ICommand接口時,將檢查Command是否可以執行的_canExecute添加到CommandManager的RequerySuggested事件中(詳細見MVVM模式解析和在WPF中的實現(三)命令綁定),這樣在出現可能會影響命令執行狀態的事件時,CommandManager會觸發事件對命令的可執行狀態進行檢查,並將檢查方法的返回值賦給命令綁定控件的IsEnable屬性上。這樣我們只要指定檢查命令執行狀態的方法即可,系統會自動檢查。不過在UWP中這個CommandManager沒有了,因此將無法再執行自動的命令可執行狀態的檢查。剛開始遇到這個問題時,按下Ctrl+.的組合鍵提示我引用WPF中的dll可以解決問題,按照提示操作了之后紅色下划線果然沒了,可是編譯時提示我有沖突還是什么,后來去網上查如何解決這個沖突,自然沒搞定。不過找到了MVVMLight的作者寫的一篇文章http://blog.galasoft.ch/posts/2015/01/re-enabling-the-commandmanager-feature-with-relaycommand-in-mvvm-light-v5/,看來CommandManager是真沒了。
為什么要在UWP中移除CommandManager呢,我也沒找到答案,個人猜測應該是這個操作浪費了太多的資源吧,特別在手機終端資源(包括電量)是很有限的。這個事件在MSDN中的描述意思也很模糊

也就是說CommandManager認為某個行為會影響到命令執行狀態就會觸發事件執行檢查。我在WPF中一直使用這個功能從未遇到過問題,由此可見能觸發這個檢查的事件或其它狀態改變還是很多的,也就是說對命令執行狀態的檢查是很頻繁的,而且當觸發檢查時會對所有注冊了狀態檢查的命令檢查一遍。可能檢查100次才會有一次狀態改變,也可能程序運行整個過程中狀態檢查結果都沒有變化,所以這個冗余操作太多了。
那么在UWP中我們應該怎么檢查命令的執行狀態呢,剛開始我打算自己寫個事件,綁定命令時把檢查執行狀態的方法訂閱那個事件,然后設置個Timer來定時觸發事件檢查執行狀態,這絕對是個餿主意,馬上就否決了。后來考慮在ViewModel中的屬性發生改變時進行檢查,也是很多冗余操作,而且這種檢查也很不完善。后來想想算了,干脆手動檢查吧,這樣檢查的針對性強。具體做法就是在命令的類型中添加觸發檢查的方法RaiseCanExecuteChanged()方法。

0x02 事件綁定到命令
其實這個問題非常好解決的,結果我卻踩到坑里一個多小時才爬出來。先說下事件綁定,再說下坑的事情。之前在WPF中將事件綁定到命令靠的是Interactivity.dll,在UWP中換了另外兩個dll:Microsoft.Xaml.Interactivity.dll和Microsoft.Xaml.Interactions.dll,過方法類似。我的VS采用的是默認安裝,這兩個dll在C:\Program Files (x86)\Microsoft SDKs\Windows\v8.1\ExtensionSDKs\BehaviorsXamlSDKManaged\12.0\References\CommonConfiguration\Neutral這個位置。添加這兩個dll的引用后就可以在XAML中實現事件綁定到命令了。下圖代碼綁定了主頁面的PointerMoved事件至ViewModel中的CmdMouseMove命令。

特別值得一提的是之前在WPF中為了在事件綁定到命令后能夠把時間的EventArgs傳給命令,我們自己寫了一個MyEventCommand類(MVVM設計模式和WPF中的實現(四)事件綁定),在UWP中就不需要了,InvokeCommandAction在不綁定CommandParameter的情況下默認就傳遞EventArgs參數,但當綁定了CommandParameter后傳給Command的則為CommandParameter綁定的對象而不是事件的EventArgs參數。
這個應該是很簡單的,結果我在測試的時候在類庫的項目中引用了那兩個dll,沒有在測試項目中引入,所以在測試項目MainPage的XAML中進行引用命名空間時總是提示我不存在。記得以前剛換Windows8.1的時候遇到過類似問題,原因是系統會把來自網絡的dll加鎖,只要手動解鎖就可以用了,翻了下之前那篇文章,也沒在win10中看到有dll加鎖啊。又開始懷疑dll版本不對,搜了好半天,最后發現是測試項目中沒有引入那兩個dll,反倒類庫中沒有必要引入。
0x03 其他一些問題
由於剛開始學習UWP開發,缺乏相關經驗,因此在WPF中的View和ViewModel的通信和依賴注入不知道是不是適合於UWP,所以只是暫時把功能移過去,有待后面邊學習邊測試邊修改。好吧,其實后面學習過程中寫測試程序的話應該也不會用MVVM的。
另外移植過程中還發現從Type創建實例的方法變了,以前是這么寫的:
type.Assembly.CreateInstance(type.FullName);
在UWP中需要這么寫:
type.GetConstructor(Type.EmptyTypes).Invoke(parameters);
還有Dispatcher變成了CoreDispatcher等其他一些問題都與MVVM無關了,而且處理起來也都很容易,就不再討論了。
0x04 示例
之前WPF版的MVVM框架還沒來得及寫示例,UWP版的反倒寫了。示例很簡單,界面如下圖所示:

說明一下:
1.上邊的TextBox顯示的是當前鼠標相對於窗體的位置,隨着鼠標移動顯示數值會變化,方法是綁定了MainPage的PointerMoved事件,這說明了命令綁定運行正常,事件綁定命令並傳遞事件的參數也運行正常。
2.鼠標位置信息正確顯示說明ViewModel中屬性的數據綁定正常。
3.下面ListView數據正確顯示說明綁定ViewModel中的集合屬性正常。
4.按下Add會插入一條數據,按下Delete會刪除選中數據,按下Clear會清空列表,說明命令綁定正常。
5.當無選中項時Delete按鈕禁用,說明命令執行狀態檢查正常。
0x05 相關下載
https://github.com/durow/AyxMVVM
