This article comes from reprint
Original author: RyzenAdorer
Original title: Modularization of. NET Core 3 WPF MVVM Framework Prism Series
Original link: www.cnblogs.com/ryzen/p/12185054.html
This article will describe how to use the modularization of applications using the MVVM framework Prism in the. NET Core3 environment
1. preface
We all know that in order to form a low-coupling and high-cohesion application, we will layer. Take a WPF program for example, we use the MVVM model to divide an application into View-ViewModel-Models, which greatly eliminates the high coupling between previous business logic and interface elements, allowing us backend developers to focus more on the business logic level, and those that belong to UI interfaces can be handed over to more professional UI personnel
However, an application is composed of different business modules. Ideally, each business module has independent functions and has a low coupling relationship with other business modules, and each business module can be used separately for development, testing and deployment. The application composed in this way is very easy to expand, test and maintain, and Prism provides the function of modularize the application
Let's take a look at the next mini Demo first

Let's take a look at the solution project:

I divided this mini demo into four projects, of which Shell Forms is the main project, then MedicineModule and PatientModule are our separated business modules, and finally Infrastructure is our public shared project. We will explain step by step how the demo is modularized.
First of all, we quoted an official diagram to roughly explain the process of creating a loading module:

- Registration/Discovery Module
- found on this
- initialization module
Let's take a look at how the demo is modularized based on this process?
2. Registration/Discovery Module
2.1. Registration module
There are three ways to register the prism module:
- code registers
- Catalog file scanning registration
- Configuration File App.config Registration
We use code registration first. First of all, we need to define modules. We create MedicineModule class and PatientModule class in two projects: PrismMetroSample.MedicineModule and PrismMetroSample.PatientModule, with the code as follows:
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)
{
}
}
2.1.1. code registers
Then we reference the PrismMetroSample.MedicineModule and PrismMetroSample.PatientModule assemblies respectively in the project of the main form of PrismMetroSample.Shell, and then register the code in 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
});
}
Note: Code registration is a part without the so-called discovery module, but is a part of direct registration
2.1.2. Catalog file scanning registration
2.1.2.1. Registration module
First of all, we add features to the MedicineModule first. OnDemand is true for "on-demand" loading, while PatientModule can be loaded without adding it by default
[Module(ModuleName = "MedicineModule", OnDemand =true)]
public class MedicineModule : IModule
Then we copy the PrismMetroSample.MedicineModule project and PrismMetroSample.PatientModule project settings generation event dll to the Modules folder under the PrismMetroSample.Shell project bin\Debug
The generate event command line is as follows:
xcopy "$(TargetDir)$(TargetName)*$(TargetExt)" "$(SolutionDir)\PrismMetroSample.Shell\bin\Debug\netcoreapp3.1\Modules\" /Y /S
2.1.2.2. discovery module
Then we overload the function in App.xaml.cs:
protected override IModuleCatalog CreateModuleCatalog()
{
//获取该路径下的文件夹的模块目录
return new DirectoryModuleCatalog() { ModulePath = @".\Modules" };
}
2.1.3. Register using the configuration file App.config
2.1.3.1. Registration module
We add an App.config file to the main form project PrismMetroSample.Shell:
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>
When startupLoaded is true, it is set to automatically load, and it is an "when available" module. If it is false, it is not loaded, and it is set to an "on-demand" module.
2.1.3.2. discovery module
Modify the CreateModuleCatalog function of App.xaml.cs:
App.xaml.cs:
protected override IModuleCatalog CreateModuleCatalog()
{
return new ConfigurationModuleCatalog();//加载配置文件模块目录
}
3. found on this
There are two ways for a prism application to load a module:
- Load "when available" modules (default)
- Load the "on-demand" module as appropriate
When registering the code, I will register the PatientModule by default, and then register the MedicineModule and set it to "on-demand" loading. One advantage of "on-demand" loading is that after the application is initialized, the MedicineModule module will not be loaded into memory, which provides a lot of flexibility. By default, we can load some "available" modules, and then we can load the modules we need "on demand" according to our own requirements
Here we can explain the code implementation of on-demand loading MedicineModule. First, we have set the MedicineModule to "on-demand" loading in App.cs, and then we use a button to load the MedicineModule in the main form. The code is as follows:
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");
}
}
We can also detect the loading module completion event. We add these sentences to MainWindowViewModule:
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}模块被加载了");
}
The effects are as follows:

4. initialization module
After loading the module, the module will be initialized. Let's take MedicineModule as an example and take a look at the code first:
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)
{
}
}
Among them, the IModule interface defines two functions, OnInitialized and RegisterTypes, where the initialization order is RegisterTypes->OnInitialized, which means that the RegisterTypes function will precede the OnInitialized function. Although I did not write code in RegisterTypes here, the pass here can be dependency injected into the container for use by the MedicineModule module. On Initialized we usually register module attempts or subscribe to application-level events and services. Here, I register the three Views in different regions for module views
Finally, in fact, at the beginning, we saw the Demo demonstration. When we clicked on the patient list, the patient details page that came out had no data. This involves communication between the forms. The patient list and the patient details page belong to the same module, which is easy to handle. How to add the searched drugs to the drug list on the current patient details page? This involves communication between different module forms. If we don't handle it properly, it will cause strong coupling between modules. In the next chapter, we will talk about how to use event aggregators to realize communication between different forms in the same module and different modules and different forms, and the complete Demo will be released in the next chapter.