.NET Core 3 WPF MVVM框架 Prism系列之模塊化

本文將介紹如何在.NET Core3環境下使用MVVM框架Prism的應用程序的模塊化html

前言

 咱們都知道,爲了構成一個低耦合,高內聚的應用程序,咱們會分層,拿一個WPF程序來講,咱們經過MVVM模式去將一個應用程序的分紅View-ViewModel-Model,大大消除以前業務邏輯和界面元素之間存在的高耦合,使咱們後臺開發人員能夠將重點更放在業務邏輯層面上,屬於UI界面的則能夠交給更專業的UI人員c#

 可是一個應用程序是由不一樣的業務模塊來組合而成,咱們理想狀態下,每一個業務模塊擁有着可以獨立的功能,而且和其餘業務模塊之間的是低耦合關係的,且每一個業務模塊能夠單獨用來開發,測試和部署,這樣組成的應用程序是很是容易擴展,測試和維護的,而Prism提供將應用程序模塊化的功能app

咱們先來看下一個小Demo 再來看看解決方案的項目: 框架

我將該小demo,分爲四個項目,其中Shell爲主窗體項目,而後MedicineModule和PatientModule爲咱們分割開的業務模塊,最後Infrastructure則爲咱們的公共共享項目,咱們將一步步講解該demo如何進行模塊化的.ide

首先,咱們引用官方的一個圖,大體講解了建立加載模塊的流程: 模塊化

  • 註冊/發現模塊
  • 加載模塊
  • 初始化模塊

咱們就根據這個流程來看看demo是如何進行模塊化的?函數

一.註冊/發現模塊

1.註冊模塊

prism註冊模塊有三種方式:測試

  • 代碼註冊
  • 目錄文件掃描註冊
  • 配置文件App.config註冊

咱們先用代碼註冊的方式,首先咱們要先定義模塊,咱們分別在PrismMetroSample.MedicineModule和PrismMetroSample.PatientModule兩個項目中建立MedicineModule類和PatientModule類,代碼以下:spa

MedicineModule.cs:命令行

public class MedicineModule : IModule
 {
     public void OnInitialized(IContainerProvider containerProvider)
     {
         var regionManager = containerProvider.Resolve<IRegionManager>();


         //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)
     {
            
     }
 }

PatientModule.cs:

public class PatientModule : IModule
 {
     public void OnInitialized(IContainerProvider containerProvider)
     {
         var regionManager = containerProvider.Resolve<IRegionManager>();

         //PatientList
         regionManager.RegisterViewWithRegion(RegionNames.PatientListRegion, typeof(PatientList));
         //PatientDetail-Flyout
         regionManager.RegisterViewWithRegion(RegionNames.FlyoutRegion, typeof(PatientDetail));
           
     }

     public void RegisterTypes(IContainerRegistry containerRegistry)
     {
           
     }
 }

1.代碼註冊

而後咱們在PrismMetroSample.Shell主窗體的項目分別引用PrismMetroSample.MedicineModule和PrismMetroSample.PatientModule程序集,以後在App.xaml.cs中代碼註冊:

protected override void ConfigureModuleCatalog(IModuleCatalog moduleCatalog)
{
   moduleCatalog.AddModule<PrismMetroSample.PatientModule.PatientModule>();
    
    //將MedicineModule模塊設置爲按需加載
   var MedicineModuleType = typeof(PrismMetroSample.MedicineModule.MedicineModule);
   moduleCatalog.AddModule(new ModuleInfo()
   {
        ModuleName= MedicineModuleType.Name,
        ModuleType=MedicineModuleType.AssemblyQualifiedName,
        InitializationMode=InitializationMode.OnDemand
    });
            
 }

注:代碼註冊是沒有所謂的發現模塊部分,是直接註冊部分

2.目錄文件掃描註冊

2.1註冊模塊

首先咱們先在MedicineModule加上特性,OnDemand爲true爲"按需"加載,而PatientModule默認加載則能夠不加

[Module(ModuleName = "MedicineModule", OnDemand =true)]
 public class MedicineModule : IModule

而後咱們將PrismMetroSample.MedicineModule項目和PrismMetroSample.PatientModule項目設置生成事件dll拷貝到PrismMetroSample.Shell項目bin\Debug下的Modules文件夾下

生成事件命令行以下:

xcopy "$(TargetDir)$(TargetName)*$(TargetExt)" "$(SolutionDir)\PrismMetroSample.Shell\bin\Debug\netcoreapp3.1\Modules\" /Y /S
2.2發現模塊

而後咱們在App.xaml.cs重載實現該函數:

protected override IModuleCatalog CreateModuleCatalog()
{
   //獲取該路徑下的文件夾的模塊目錄
   return new DirectoryModuleCatalog() { ModulePath = @".\Modules" };
}

3.使用配置文件App.config註冊

3.1註冊模塊

咱們在主窗體項目PrismMetroSample.Shell添加一個App.config文件:

App.config:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <configSections>
    <section name="modules" type="Prism.Modularity.ModulesConfigurationSection, Prism.Wpf"/>
  </configSections>
  <modules>
    <!--註冊PatientModule模塊-->
    <module assemblyFile="PrismMetroSample.PatientModule.dll" moduleType="PrismMetroSample.PatientModule.PatientModule, PrismMetroSample.PatientModule, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" moduleName="PatientModule" startupLoaded="True" />
    <!--註冊MedicineModule模塊-->
    <module assemblyFile="PrismMetroSample.MedicineModule.dll" moduleType="PrismMetroSample.MedicineModule.MedicineModule, PrismMetroSample.MedicineModule, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" moduleName="MedicineModule" startupLoaded="false" />
  </modules>
</configuration>

其中startupLoaded爲true則設置自動加載,爲"可用時"模塊,爲false則不加載,設置爲「按需」模塊

3.2發現模塊

修改App.xaml.cs的CreateModuleCatalog函數: App.xaml.cs:

protected override IModuleCatalog CreateModuleCatalog()
 {
    return new ConfigurationModuleCatalog();//加載配置文件模塊目錄
 }

二.加載模塊

prism應用程序加載模塊有兩種方式:

  • 加載「可用時」的模塊(默認方式)
  • 根據狀況加載「按需」模塊

 在代碼註冊時候,我將經過默認方式註冊了PatientModule,而後註冊MedicineModule將其設置爲"按需"加載,「按需」加載有個好處就是,應用程序運行初始化後,MedicineModule模塊是不加載到內存的,這樣就提供了很大的靈活空間,默認咱們能夠加載一些"可用"的模塊,而後咱們能夠根據自身要求去"按需"加載咱們所須要的模塊

 這裏能夠講解下按需加載MedicineModule的代碼實現,首先咱們已經在App.cs中將MedicineModule設置爲"按需"加載,而後咱們在主窗體經過一個按鈕去加載MedicineModule,代碼以下: MainWindowViewModle.cs:

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");
    }
 }

咱們還能夠去檢測加載模塊完成事件,咱們MainWindowViewModle中加上這幾句:

IModuleManager _moduleManager;
public MainWindowViewModel(IModuleManager moduleManager)
{
   _moduleManager = moduleManager;
   _moduleManager.LoadModuleCompleted += _moduleManager_LoadModuleCompleted;
}

private void _moduleManager_LoadModuleCompleted(object sender, LoadModuleCompletedEventArgs e)
{
   MessageBox.Show($"{e.ModuleInfo.ModuleName}模塊被加載了");
}

效果以下:

三.初始化模塊

加載模塊後,模塊就會進行初始化,咱們以MedicineModule爲例子,先來看看代碼:

public class MedicineModule : IModule
 {
     public void OnInitialized(IContainerProvider containerProvider)
     {
         var regionManager = containerProvider.Resolve<IRegionManager>();


         //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)
     {
            
     }
 }

 其中,IModule接口定義了兩個函數OnInitialized和RegisterTypes,其中初始化順序是RegisterTypes->OnInitialized,也就是RegisterTypes函數會先於OnInitialized函數,雖然這裏我沒在RegisterTypes寫代碼,可是這裏經過是能夠依賴注入到容器,給MedicineModule模塊使用的,而OnInitialized咱們一般會註冊模塊試圖,或者訂閱應用程序級別的事件和服務,這裏我是將三個View分別分區域註冊模塊視圖

 最後,其實一開始咱們看到Demo演示,點擊病人列表,出來的病人詳細頁是沒有數據的,這涉及到窗體之間的通信,病人列表和病人詳細頁屬於同一模塊,這很好辦,如何我要將搜索到的藥物加到當前病人詳細頁的藥物列表裏面,這就涉及到不一樣模塊窗體之間的通信,處理很差是會形成模塊之間的強耦合,下篇咱們會講到如何使用事件聚合器來實現同一模塊不一樣窗體的通信和不一樣模塊不一樣窗體的通信,而完整的Demo也會在下一篇放出。

原文出處:https://www.cnblogs.com/ryzen/p/12185054.html

相關文章
相關標籤/搜索