在WPF中使用依賴註入的方式創建視圖

来源:http://www.cnblogs.com/durow/archive/2016/08/25/5806499.html
-Advertisement-
Play Games

在WPF中使用依賴註入的方式創建視圖 0x00 問題的產生 互聯網時代桌面開發真是越來越少了,很多應用都轉到了瀏覽器端和移動智能終端,相應的軟體開發上的新技術應用到桌面開發的文章也很少。我之前主要做WPF,今年開始學習Web應用開發,於是就接觸到了.NET Core,其中的很多概念很值得在桌面開發中 ...


在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作為依賴註入容器。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 這幾天無意間看到一個關於分頁的帖子,覺得寫得挺好的。關於這些東西,自己一直都是只知道原理,卻沒有真正動手做過,於是研究了一下分頁的原理自己動手寫了一個十分特別非常簡單的分頁程式,在這裡與大家分享一下。 這個程式取數據使用的ado.net,首先先新建一個取數據的類PageDAl 然後記得修改一下web ...
  • 前言 在開發應用程式時,通常只讓程式運行一個實例。所以,就要判斷程式是否已經運行。 下麵是我自己在項目中使用到,封裝好的幫助類。有 普通的 C 應用程式 和 Windows CE 和 Windows Mobile 應用程式使用的方法。 主要是通過 System.Threading.Mutex 類 和 ...
  • Razor是MVC框架視圖引擎,我們今天就來說一說Razor視圖引擎。 首先還是來創建一個基礎項目叫Razor來演示。 先來定義一個Model叫Product 接著,來定義一個控制器。 最後,在Views/Home文件夾中創建Index.cshtml文件。 1. 使用模型對象 我們在視圖的第一行使用 ...
  • 舉幾個值類型和引用類型的記憶體配置: 值類型存儲在棧中,引用類型堆里: 1,數組 數組是引用類型,但是數組的元素可以是值類型或引用類型 2. 結構 結構是值類型,簡略的看個例子 struct sample { public int X; public int Y; } simple ss = new ...
  • ASP.NET Core基於 .NET Core 項目模型,它支持構建能夠運行在 Windows、Mac和 Linux 上的跨平臺應用程式。當您構建一個 .Net Core 項目的時候,您可以選擇一種 .NET框架來構建您的應用程式,.NET Framework (CLR)、 .NET Core (... ...
  • LINQ對於筆者來說, 優美而濃縮的代碼讓人震驚. 研究LINQ就是在藝術化自己的代碼. 之前只是走馬觀花學會了基本的語法, 但是經常在CSDN看到令人驚訝自嘆不如的LINQ代碼, 還是讓人羡慕嫉妒恨, 真有一種心血來潮想追趕的衝動. 所以, 還是決定系統的學習一下LINQ. (1.4) LINQ ...
  • WCF如何實現對於Rest支持的呢?弄清這一點是學習Rest WCF的關鍵。 為了實現於對Rest的支持,在 .NET Framework 中,WCF 在 System.ServiceModel.Web 組件中新增了編程模型和一些基礎架構部件。WCF Web編程模型幾個重要類型就是: ...
  • 自古以來,人類的進步都是依賴於工具的進步,從刀耕火種,到使用青銅器,再到現在的科技,每一次都使我們的工作效率提高了無數倍,所以一個好的工具能使我們提高無數倍的工作效率,下麵,我就根據自己簡單的總結一下我們到底要有什麼樣的裝備。這裡現在只寫必須的,因為有太多的好的軟體了,以後再分類補充吧。 一,操作系 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...