本文大綱
1、Prism框架下載和說明
2、Prism項目預覽及簡單介紹。
3、Prism框架如何在項目中使用。
Prism框架下載和說明
Prism框架是針對WPF和Silverlight的MVVM框架,這個大家應該之前,都有所耳聞,關於該框架的具體說明,可以參考如下地址:
Prism框架通過功能模塊化的思想,來講復雜的業務功能和UI耦合性進行分離,通過模塊化,來最大限度的降低耦合性,很適合我們
進行類似插件話的思想來組織系統功能。並且模塊之間,通過發布和訂閱事件來完成信息的通信。而且其開放性支持多種框架集成。
Prism項目預覽及簡單介紹
框架下載完畢后,解壓后的文件的組織模式如下:
我們先打開Hello World QuickStart.bat看看
上面是項目的組織結構,關於該項目內部的代碼結構和寫法,我們來一一分析和解釋。
A、先看看HelloworldModule的代碼和內容。
Views文件夾中包含了UI視圖界面內容。
其中只是包含了一個Textbox文本控件,其他沒有太多的內容。
接着看看該設計文件對應的后台cs文件中的代碼。
也是沒有什么特別的內容。接着我們看看Module中的內容代碼:
上面對於Module中的代碼,我們就簡單的分析完畢了,當然這個模塊沒有辦法獨立的運行,我們肯定要將模塊加載到宿主或某個控制的主界面中,把它顯示出來即可,下面我們就來看看Prism最關鍵的部分。
B、宿主或主界面。
先看看APP文件
設計視圖中未指定,那么肯定是在cs文件中的某處直接或簡介指定。
果然,這里采用了BootStrapper來完成Run方法,實現應用的啟動,我們可以來深挖,看看該文件中都包含什么內容。
接着,我們來看看Shell中的內容:
我們在來看看shell里面有沒有什么特殊的代碼,打開后台cs文件
並無任何特殊的內容。所以我們可以大概的了解到了Prism的運行機制和流程,那么運行后的效果如下:
符合預期的目標,下面我們將繼續深入的挖掘Prism的強大之處。
Prism框架如何在項目中使用
Prism是一個強大的Mvvm框架,下面我們將重點講解如何在項目使用Prism提供的基礎功能,完成基於MVVM的WPF項目的框架設計和開發,包括應用程序的架構。
項目的解決方案結構,項目采用Prism作為UI框架,NHiberia+Unity作為ORM和IOC框架。
下面我們就來一步步解析項目中的每個部分的細節和最終項目如何把這些細節組織起來的做一個整體結構上的說明。關於其他的分層設計結構我就不多說了,只關注Prism部分的內容。
1、關於對Prism的基礎封裝
為什么不直接使用Prism,我們希望開發人員的學習成本更低,所以,我們隊Prism的一些方法進行了封裝,更符合開發人員之前熟悉的MVVM模式。
關於封裝的具體內容,我們后續會看到代碼。
2、關於Infrastructure基礎設施層定義
3、具體的模塊定義
4、看看程序應用宿主的定義:
通過上面,我們介紹了基礎的項目和具體的模塊和宿主模塊的定義,下面我們就來詳細的分析下Prism如何加載模塊的並且模塊間如何通信,如何完成業務功能的完整流程:
在之前介紹HelloWorld的時候,我們有簡單的介紹了Prism的基本流程是宿主會在Bootstrappter中對模塊進行裝載並初始化,下面我們來看看我們在我給出的例子中的具體過程。
a、Shell的定義:
與之前的區別就是在於,我們原來是手寫的字符串,這里通過單獨的類定義成靜態的常量成員,我們能夠防止名稱出錯的可能。同時我們也可以避免因為某處界面上Region符號的變化,因為某處沒有修改,而造成不同步,運行出錯的情況的發生,更容易統一的管理。具體的基礎設施層中關於RegionType的定義如下:
接着查看Shell的后台cs代碼:
1 /// <summary> 2 /// MainWindow.xaml 的交互邏輯 3 /// </summary> 4 [Export] 5 public partial class Shell : Window 6 { 7 public Shell() 8 { 9 InitializeComponent(); 10 } 11 12 /// <summary> 13 /// 設置ViewModel 14 /// </summary> 15 /// <remarks> 16 /// This set-only property is annotated with the <see cref="ImportAttribute"/> so it is injected by MEF with 17 /// the appropriate view model. 18 /// </remarks> 19 [Import] 20 [SuppressMessage("Microsoft.Design", "CA1044:PropertiesShouldNotBeWriteOnly", Justification = "Needs to be a property to be composed by MEF")] 21 ShellViewModel ViewModel 22 { 23 set 24 { 25 this.DataContext = value; 26 if (this.DataContext != null) 27 { 28 ((ShellViewModel)this.DataContext).OnStatusChanged += new Action<string>(SystemStatusManagementEventHandler); 29 } 30 } 31 } 32 33 public void SystemStatusManagementEventHandler(string parameter) 34 { 35 if (parameter.IsNullOrEmpty()) 36 { 37 throw new ArgumentNullException("無法完成操作"); 38 } 39 40 switch (parameter) 41 { 42 case HM_EMSTS.WorkStation.Infrastructure.MenuParams.Max: 43 this.WindowState = System.Windows.WindowState.Maximized; 44 break; 45 case HM_EMSTS.WorkStation.Infrastructure.MenuParams.Min: 46 this.WindowState = System.Windows.WindowState.Minimized; 47 break; 48 case HM_EMSTS.WorkStation.Infrastructure.MenuParams.Close: 49 if (MessageBox.Show("是否退出系統?", "退出系統?", MessageBoxButton.OKCancel, MessageBoxImage.Question) == MessageBoxResult.OK) 50 { 51 this.Close(); 52 } 53 break; 54 } 55 } 56 }
上面的代碼中采用了MEF中的Export特性和Import特性。 關於MEF的內容,我這里就不多介紹了,不是很了解的可以谷歌或百度下。
繼續,我們查看Shell的ViewModel定義,因為上面的后台的cs代碼中有訂閱相關的事件。
1 [Export(typeof(ShellViewModel))] 2 public class ShellViewModel : HM_EMSTS.WorkStation.UICommon.NotifyBaseObject 3 { 4 public Action<string> OnStatusChanged; 5 6 [ImportingConstructor] 7 public ShellViewModel(IEventAggregator eventAggregator) 8 { 9 //注冊事件 10 if (eventAggregator == null) 11 { 12 throw new ArgumentNullException("eventAggregator"); 13 } 14 15 eventAggregator.GetEvent<HM_EMSTS.WorkStation.Infrastructure.Events.SystemStatusManagementEvent>().Subscribe(this.SystemStatusManagementEventHandler); 16 } 17 18 public void SystemStatusManagementEventHandler(string parameter) 19 { 20 if (parameter.IsNullOrEmpty()) 21 { 22 throw new ArgumentNullException("無法完成操作"); 23 } 24 25 if (OnStatusChanged != null) 26 OnStatusChanged(parameter); 27 } 28 }
上面的代碼,主要是為了完成對事件的訂閱,並且當收到訂閱的事件時,通知出去。這里特別注意,可以參考下圖:
關於Event的定義我們可以看看上述Event的定義:
如果想按照,我們之前寫的那樣的形式來綁定和觸發事件操作的話,必須這么寫。
那么下面我們來看看ShellModule的定義吧,我們這里的代碼如下:
我們使用了某個Module項目中的頁面來替換shell中的Region。這樣保證了Shell運行起來后能夠正確的顯示界面。
下面來看看項目中最重要的WorkStationBootstrapper的定義
前面介紹的helloWorld里面是采用的Unity容器,這里是MEF,所以要注意的部分,有所不同。這里需要制定MEF可導入導出部件所在的目錄或程序集
我們知道shell后台cs的代碼定義前面也說過了,有帶有export標記。那么當執行上述的代碼后,將會出現在MEFbootstrappter的Container中。這里的container是CompositionContainer是MEF中定義的。
接着查看如下方法:
通過上面的幾個方法,此時,我們的主程序,就完成了對Region的解析,顯示出來即可。
B、模塊定義:
Module主要是為了,替換Region符合和標記為具體的界面而是用的。
我們下面挑選一個頁面來展示完整的定義和操作。
1、Model定義:
當我們的Model具有自動通知機制時,特別對於列表中的某個單元格的屬性發生改變后,不需要刷新整個列表,這時候就會自動完成更新,WPF會自動完成。
2、IView接口定義。
因為我們這里采用MVP的設計模式,所以要求所有的View必須繼承自IView接口。
我們這里都是直接定義View對應的唯一接口即可,主要是為了MEF的Export和Import時有用。
3、View的定義。
設計視圖:
后台代碼:
4、ViewModel的定義。
這里由於我們采用MVP模式,所有對於不同View之間的交互,我們這里放到了Presenter中,ViewModel充當的是對IView界面的完全控制抽象。
所以我們看到這里,沒有任何的業務代碼。但是對已IView界面所有的綁定信息,都需要定義到該類中。
5、Presenter定義。
上面講Presenter標記了Export。主要是在Module中對Region進行映射時使用。
然后我們來看看PresenterBase的定義,一看便明白
這樣在構造展示器時,我們便可以將IView和ViewModel之間的關系完成綁定。
6、Module的定義
這樣我們就完成了,一個模塊的功能開發,該功能模塊盡量功能獨立。
最終,我們通過一個主界面,將這樣功能模塊組裝起來即可。
最終
將上面構建的模塊運行下,看看效果,也許效果不是很好看,沒有設置樣式。
程序運行的框架還是非常的清晰,上面是工具欄,菜單欄,內容區。通過Prism我們可以講菜單欄或者工具欄中的功能都設計成獨立的模塊,分別進行裝載和控制,這樣能夠具有非常好的擴展性和可維護性。