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

来源:https://www.cnblogs.com/ryzen/archive/2020/01/13/12185054.html

本文將介紹如何在.NET Core3環境下使用MVVM框架Prism的應用程式的模塊化 前言 我們都知道,為了構成一個低耦合,高內聚的應用程式,我們會分層,拿一個WPF程式來說,我們通過MVVM模式去將一個應用程式的分成View ViewModel Model,大大消除之前業務邏輯和界面元素之間存在 ...


本文將介紹如何在.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:

 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也會在下一篇放出。


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

更多相關文章
  • 伴隨著dotnet core的不斷迭代,我們在享受.net性能上的提升之外,還收穫了許許多多新出現的API。不知您有沒有發現,有這樣一個類型在開始逐漸出現在我們的視野中 ———— ValueTask ...
  • 問題描述 最近在使用ef core連接oracle的發現Find、FirstOrDefault、Skip Task分頁等等方法執行失敗。使用的是docker安裝的oracle11,錯誤如下圖: 解決辦法 使用builder.UseOracleSQLCompatibility("11")方法來指定or ...
  • 一、什麼是Lock? Lock——字面上理解就是鎖上;鎖住;把……鎖起來的意思; 為什麼要鎖?要鎖乾什麼?——回到現實中可想象到,這個衛生間我要上,其他人不要進來!(所以我要鎖住門);又或者土味情話所言,我要把你鎖在我的心裡,然後在裡面加個無限迴圈語句,不給你出來,也不被別人所得,你只能是我的,哈哈 ...
  • 微信公眾號: "Dotnet9" ,網站: "Dotnet9" ,問題或建議: "請網站留言" , 如果對您有所幫助: "歡迎贊賞" 。 C WPF從RIOT API獲取數據(RIOT代表作品《英雄聯盟》) 閱讀導航 1. 本文背景 2. 代碼實現 3. 本文參考 1. 本文背景 RIOT(拳頭)是 ...
  • 本文介紹通過調用Spire.Cloud.Word.SDK提供的ConvertApi介面將Word轉換為PDF、XPS、Epub、RTF以及將Docx轉為Doc格式等。調用介面方法及步驟參考以下步驟: 步驟一:dll文件獲取及導入。通過官網本地下載SDK文件包。(須在e-iceblue中國官網線上編輯 ...
  • 2017年12月份,我離開北京,回到了武漢,開始在現在這家公司擔任架構師工作。經過2年的時間,逐步完成以.net core+k8s為核心的技術架構。文末有彩蛋。 以下整理這兩年的主要時間節點: 2018年1月到2018年3月 基於.net core的底層框架的封裝,封裝了數據訪問,緩存,消息隊列,加 ...
  • 1、C# 的三大特性? 封裝、繼承、多態 2、簡述 private、 protected、 public、 internal 修飾符的訪問許可權。 private : 私有成員, 在類的內部才可以訪問。 protected : 保護成員,該類內部和繼承類中可以訪問。 public : 公共成員,完全公 ...
  • 一、前言 上一篇《asp.net core 3.x 通用主機原理及使用》扯了下3.x中的通用主機,剛好有哥們寫了篇《.NET Core 3.1和WorkerServices構建Windows服務》可以當做通用主機的案例來看。本篇主要聊下asp.net core 3.x中是如何使用通用主機來承載asp ...
一周排行
  • 微信公眾號: "Dotnet9" ,網站: "Dotnet9" ,問題或建議: "請網站留言" , 如果對您有所幫助: "歡迎贊賞" 。 .NET CORE(C ) WPF 抽屜式菜單 閱讀導航 1. 本文背景 2. 代碼實現 3. 本文參考 4. 源碼 1. 本文背景 使用簡單動畫實現抽屜式菜單 ...
  • 在上面abp(net core)+easyui+efcore實現倉儲管理系統——ABP WebAPI與EasyUI結合增刪改查之八(三十四) 文章的學習之後。我們通過前面的八篇文章已經學習了通過WebAPI介面與控制器去實現新增、刪除與修改功能。接下來,我們要在控制器中實現查詢功能。 ...
  • 1.選中項目-->屬性-->生成-->選中 XML文檔文件(xml路徑和該項目相同) 2.選擇生成序列化程式集:自動/開 ...
  • 本筆記摘抄自:https://www.cnblogs.com/PatrickLiu/p/7640873.html,記錄一下學習過程以備後續查用。 一、引言 從今天開始我們開始講結構型設計模式,結構型設計模式有如下幾種:適配器模式、橋接模式、裝飾模式、組合模式、外觀模式、享元模式、代理模式。 創建型設 ...
  • C 中 ConfigureAwait 相關答疑FAQ 在前段時間經常看到園子里有一些文章討論到 ConfigureAwait,剛好今天在微軟官方博客看到了 "Stephen Toub" 前不久的一篇答疑 ConfigureAwait 的一篇文章,想翻譯過來。 原文地址:https://devblog ...
  • 想要實現二維數組中根據某個欄位排序,一般可以通過數組迴圈對比的方式實現。這裡介紹一種更簡單的方法,直接通過PHP函數實現。array_multisort() :可以用來一次對多個數組進行排序,或者根據某一維或多維對多維數組進行排序。詳細介紹可參考PHP手冊:https://www.php.net/m ...
  • 常用的軟體: 播放器: cloundMusic(網易雲音樂) https://music.163.com/#/download PotPlayer(一款強大的視頻播放器) https://daumpotplayer.com/download/ ACDsee(ACDsee圖片編輯器免費版) https ...
  • 發現問題 在一次偶然中,在爬取某個網站時,老方法,打開調試工具查看請求方式,請求攔截,是否是非同步載入,不亦樂乎,當我以為這個網站非常簡單的時候,發現二級網頁的地址和源碼不對應 Ajax非同步載入?源碼也是這樣的 而且這些鏈接直... ...
  • 準備年後要跳槽,所以最近一直再看面試題,並且把收集到的面試題整理了以下發到博客上,希望對大家有所幫助。 首先是集合類的面試題 1. HashMap 排序題,上機題。 已知一個 HashMap<Integer,User>集合, User 有 name(String)和 age(int)屬性。請寫一個方 ...
  • JVM體繫結構圖 Native Interface(本地介面) Java本地介面(Java Native Interface (JNI))允許運行在Java虛擬機(Java Virtual Machine (JVM))上的代碼調用本地程式和類庫,或者被它們調用,這些程式和類庫可以是其它語言編寫的,比 ...
x