本文將介紹如何在.NET Core3環境下使用MVVM框架Prism的應用程序的模塊化
前言#
我們都知道,為了構成一個低耦合,高內聚的應用程序,我們會分層,拿一個WPF程序來說,我們通過MVVM模式去將一個應用程序的分成View-ViewModel-Model,大大消除之前業務邏輯和界面元素之間存在的高耦合,使我們後臺開發人員可以將重點更放在業務邏輯層面上,屬於UI界面的則可以交給更專業的UI人員
但是一個應用程序是由不同的業務模塊來組合而成,我們理想狀態下,每個業務模塊擁有著能夠獨立的功能,並且和其他業務模塊之間的是低耦合關係的,且每個業務模塊可以單獨用來開發,測試和部署,這樣組成的應用程序是非常容易擴展,測試和維護的,而Prism提供將應用程序模塊化的功能
我們先來看下一個小Demo
再來看看解決方案的項目:
我將該小demo,分為四個項目,其中Shell為主窗體項目,然後MedicineModule和PatientModule為我們分割開的業務模塊,最後Infrastructure則為我們的公共共享項目,我們將一步步講解該demo如何進行模塊化的.
首先,我們引用官方的一個圖,大致講解了創建加載模塊的流程:
- 註冊/發現模塊
- 加載模塊
- 初始化模塊
我們就根據這個流程來看看demo是如何進行模塊化的?
一.註冊/發現模塊#
1.註冊模塊#
prism註冊模塊有三種方式:
- 代碼註冊
- 目錄文件掃描註冊
- 配置文件App.config註冊
我們先用代碼註冊的方式,首先我們要先定義模塊,我們分別在PrismMetroSample.MedicineModule和PrismMetroSample.PatientModule兩個項目中創建MedicineModule類和PatientModule類,代碼如下:
MedicineModule.cs:
<code>Copy public class MedicineModule : IModule { public void OnInitialized(IContainerProvider containerProvider) { var regionManager = containerProvider.Resolve(); //MedicineMainContent regionManager.RegisterViewWithRegion(RegionNames.MedicineMainContentRegion, typeof(MedicineMainContent)); //SearchMedicine-Flyout regionManager.RegisterViewWithRegion(RegionNames.FlyoutRegion, typeof(SearchMedicine)); //rightWindowCommandsRegion regionManager.RegisterViewWithRegion(RegionNames.ShowSearchPatientRegion, typeof(ShowSearchPatient)); } public void RegisterTypes(IContainerRegistry containerRegistry) { } } /<code>
PatientModule.cs:
<code>Copy public class PatientModule : IModule { public void OnInitialized(IContainerProvider containerProvider) { var regionManager = containerProvider.Resolve(); //PatientList regionManager.RegisterViewWithRegion(RegionNames.PatientListRegion, typeof(PatientList)); //PatientDetail-Flyout regionManager.RegisterViewWithRegion(RegionNames.FlyoutRegion, typeof(PatientDetail)); } public void RegisterTypes(IContainerRegistry containerRegistry) { } } /<code>
1.代碼註冊#
然後我們在PrismMetroSample.Shell主窗體的項目分別引用PrismMetroSample.MedicineModule和PrismMetroSample.PatientModule程序集,之後在App.xaml.cs中代碼註冊:
<code>Copyprotected override void ConfigureModuleCatalog(IModuleCatalog moduleCatalog) { moduleCatalog.AddModule(); //將MedicineModule模塊設置為按需加載 var MedicineModuleType = typeof(PrismMetroSample.MedicineModule.MedicineModule); moduleCatalog.AddModule(new ModuleInfo() { ModuleName= MedicineModuleType.Name, ModuleType=MedicineModuleType.AssemblyQualifiedName, InitializationMode=InitializationMode.OnDemand }); } /<code>
注:代碼註冊是沒有所謂的發現模塊部分,是直接註冊部分
2.目錄文件掃描註冊#
2.1註冊模塊
首先我們先在MedicineModule加上特性,OnDemand為true為"按需"加載,而PatientModule默認加載則可以不加
<code>Copy [Module(ModuleName = "MedicineModule", OnDemand =true)] public class MedicineModule : IModule /<code>
然後我們將PrismMetroSample.MedicineModule項目和PrismMetroSample.PatientModule項目設置生成事件dll拷貝到PrismMetroSample.Shell項目bin\Debug下的Modules文件夾下
生成事件命令行如下:
<code>Copyxcopy "$(TargetDir)$(TargetName)*$(TargetExt)" "$(SolutionDir)\PrismMetroSample.Shell\bin\Debug\netcoreapp3.1\Modules" /Y /S /<code>
2.2發現模塊
然後我們在App.xaml.cs重載實現該函數:
<code>Copyprotected override IModuleCatalog CreateModuleCatalog() { //獲取該路徑下的文件夾的模塊目錄 return new DirectoryModuleCatalog() { ModulePath = @".\Modules" }; } /<code>
3.使用配置文件App.config註冊#
3.1註冊模塊
我們在主窗體項目PrismMetroSample.Shell添加一個App.config文件:
App.config:
<code>Copy /<code>
其中startupLoaded為true則設置自動加載,為"可用時"模塊,為false則不加載,設置為“按需”模塊
3.2發現模塊
修改App.xaml.cs的CreateModuleCatalog函數:App.xaml.cs:
<code>Copy protected override IModuleCatalog CreateModuleCatalog() { return new ConfigurationModuleCatalog();//加載配置文件模塊目錄 } /<code>
二.加載模塊#
prism應用程序加載模塊有兩種方式:
- 加載“可用時”的模塊(默認方式)
- 根據情況加載“按需”模塊
在代碼註冊時候,我將通過默認方式註冊了PatientModule,然後註冊MedicineModule將其設置為"按需"加載,“按需”加載有個好處就是,應用程序運行初始化後,MedicineModule模塊是不加載到內存的,這樣就提供了很大的靈活空間,默認我們可以加載一些"可用"的模塊,然後我們可以根據自身要求去"按需"加載我們所需要的模塊
這裡可以講解下按需加載MedicineModule的代碼實現,首先我們已經在App.cs中將MedicineModule設置為"按需"加載,然後我們在主窗體通過一個按鈕去加載MedicineModule,代碼如下:MainWindowViewModle.cs:
<code>Copy public class MainWindowViewModel : BindableBase { IModuleManager _moduleManager; public MainWindowViewModel(IModuleManager moduleManager) { _moduleManager = moduleManager; } private DelegateCommand _loadPatientModuleCommand; public DelegateCommand LoadPatientModuleCommand => _loadPatientModuleCommand ?? (_loadPatientModuleCommand = new DelegateCommand(ExecuteLoadPatientModuleCommand)); void ExecuteLoadPatientModuleCommand() { _moduleManager.LoadModule("MedicineModule"); } } /<code>
我們還可以去檢測加載模塊完成事件,我們MainWindowViewModle中加上這幾句:
<code>CopyIModuleManager _moduleManager; public MainWindowViewModel(IModuleManager moduleManager) { _moduleManager = moduleManager; _moduleManager.LoadModuleCompleted += _moduleManager_LoadModuleCompleted; } private void _moduleManager_LoadModuleCompleted(object sender, LoadModuleCompletedEventArgs e) { MessageBox.Show($"{e.ModuleInfo.ModuleName}模塊被加載了"); } /<code>
效果如下:
三.初始化模塊#
加載模塊後,模塊就會進行初始化,我們以MedicineModule為例子,先來看看代碼:
<code>Copy public class MedicineModule : IModule { public void OnInitialized(IContainerProvider containerProvider) { var regionManager = containerProvider.Resolve(); //MedicineMainContent regionManager.RegisterViewWithRegion(RegionNames.MedicineMainContentRegion, typeof(MedicineMainContent)); //SearchMedicine-Flyout regionManager.RegisterViewWithRegion(RegionNames.FlyoutRegion, typeof(SearchMedicine)); //rightWindowCommandsRegion regionManager.RegisterViewWithRegion(RegionNames.ShowSearchPatientRegion, typeof(ShowSearchPatient)); } public void RegisterTypes(IContainerRegistry containerRegistry) { } } /<code>
其中,IModule接口定義了兩個函數OnInitialized和RegisterTypes,其中初始化順序是RegisterTypes->OnInitialized,也就是RegisterTypes函數會先於OnInitialized函數,雖然這裡我沒在RegisterTypes寫代碼,但是這裡通過是可以依賴注入到容器,給MedicineModule模塊使用的,而OnInitialized我們通常會註冊模塊試圖,或者訂閱應用程序級別的事件和服務,這裡我是將三個View分別分區域註冊模塊視圖
最後,其實一開始我們看到Demo演示,點擊病人列表,出來的病人詳細頁是沒有數據的,這涉及到窗體之間的通訊,病人列表和病人詳細頁屬於同一模塊,這很好辦,如何我要將搜索到的藥物加到當前病人詳細頁的藥物列表裡面,這就涉及到不同模塊窗體之間的通訊,處理不好是會造成模塊之間的強耦合,下篇我們會講到如何使用事件聚合器來實現同一模塊不同窗體的通訊和不同模塊不同窗體的通訊,而完整的Demo也會在下一篇放出。
作者: RyzenAdorer
出處:https://www.cnblogs.com/ryzen/p/12185054.html
關鍵字: PrismMetroSample 註冊 App