二、从GitHub浏览Prism示例代码的方式入门WPF下的Prism之Modules的几种加载方式
这一篇梳理Prism中07示例Module的几种加载方式。
07示例分为了5个,有5种不同的Module加载方式。
我们开始学习加载Modules
观察07-Modules-Appconfig示例
分为ModuleA工程和Modules工程
我们在解决方案上打开管理解决方案的Nuget程序包,ModuleA工程引用了Prism.Wpf;Modules引用了Prism.Unity;
Modules的App.config下配置文件被修改了。我们先不分析,就看一下结构。
<configuration>
<configSections>
<section name="modules" type="Prism.Modularity.ModulesConfigurationSection, Prism.Wpf" />
</configSections>
<startup>
</startup>
<modules>
<module assemblyFile="ModuleA.dll" moduleType="ModuleA.ModuleAModule, ModuleA, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" moduleName="ModuleAModule" startupLoaded="True" />
</modules>
</configuration>
1、ModuleA工程
ModuleA工程引用了Prism.Wpf。
1.1、新建ModuleAModule.cs
ModuleAModule类继承自IModule,该接口包含2个 方法OnInitialized和RegisterTypes;ModuleAModule中实现OnInitialized方法时使用了IContainerProvider调用了Resolve
还有印象在第一篇中我们整理的,IRegionManager是一个区域管理,用于绑定区域和视图的,而这里就在做把ViewA使用regionManager的RegisterViewWithRegion()方法,向ContentRegion字符串对应的区域注册ViewA视图。通过上一篇的学习,我们知道这样就能直接把ViewA放在ContentRegion的区域,我们先不管逻辑实现。因为我们看到了配置文件中有这一块的内容,先不看。
var regionManager = containerProvider.Resolve<IRegionManager>();
regionManager.RegisterViewWithRegion("ContentRegion", typeof(ViewA));
1.2、新建Views下的ViewA自定义控件,在Views的ViewA中只有一个显示控件显示了View A 字号为38。
2、Modules工程
Modules工程引用了Prism.Unity。
2.1App.xaml
添加命名空间xmlns:prism="http://prismlibrary.com/"
修改Application为prism:PrismApplication
取消StartupUri属性
2.2App.cs
继承类由Application修改为PrismApplication
重写CreateShell()方法,通过Container.Resolve解析MainWindow并返回,作为启动窗体;
重写RegisterTypes方法
重写CreateModuleCatalog(),返回一个ConfigurationModuleCatalog()对象。
protected override IModuleCatalog CreateModuleCatalog()
{
return new ConfigurationModuleCatalog();
}
2.3Views下的MainWindow.xaml
有一个ContentControl显示控件,设置了附加依赖项属性,区域名称为ContentRegion。MainWindow.cs无特殊修改。
2.4运行代码,界面显示View A
总结:在Modules中做修改了App.config,设置了ConfigSections,我按照命名空间找过去,是使用ConfigurationStore配置Modules的。同时在App.config中也有配置的modules。里面写了assemblyFile、moduleType、moduleName、startupLoaded。也就是说这种方式是使用config配置文件的方式加载Modules,然后再对应的ModuleA或者其他名称的DLL中就可以通过containerProvider,和RegionManager来设置区域和视图的关联,但是客户端软件不推荐这种方法,因为客户端安装再客户电脑上,他可以通过自己修改app.config可以加载不同的模块。这样彻底解耦了DLL和主工程的引用关系。
观察07-Modules-Code示例
同样分为ModuleA和Modules两个工程,Modules工程引用了Prism.Unity;ModuleA工程引用了Prism.Wpf;
1、ModuleA工程
ModuleA工程引用了Prism.Wpf
1.1、新建ModuleAModule.cs
ModuleAModule类继承自IModule接口。并实现了该接口的2个方法,OnInitialized()、RegisterTypes,并再OnInitialized()方法中使用容器代理解析了IRegionManager,然后关联区域和显示界面。用于呈现。
1.2、Views下的ViewA.xaml
只包含了显示控件,显示View A,字号为38;
2、Modules工程
Moudules工程引用了Prism.Unity、ModuleA两个工程;
2.1、App.xaml
添加命名空间:xmlns:prism="http://prismlibrary.com/"
修改了Application为PrismApplication
去掉了StartupUri属性
2.2App.cs
App修改继承自PrismApplication
重写CreateShell()方法使用Container.Resolve解析MainWindow作为启动窗体;
重写RegisterTypes(不重写编译就报错)
重写ConfigureModuleCatalog方法
protected override void ConfigureModuleCatalog(IModuleCatalog moduleCatalog)
{
moduleCatalog.AddModule<ModuleA.ModuleAModule>();
}
使用传入的moduleCatalog对象的AddModule方法,传入了ModuleA工程的ModuleAModule类型。
对比了上一篇appconfig篇,我们发现再App.cs中的ConfigureModuleCatalog方法是加载Module的。不同的加载类型,这里都需要重写这个方法。而使用Code加载Module的方法还需要引用工程。
2.3Views下的MainWindow.xaml
设置了ContentControl显示控件,并设置了附加依赖项属性用于设置区域名称为ContentRegion,后台cs中无额外代码
2.4运行Modules工程
界面显示View A。
总结:创建了ModuleA工程、Modules主工程,ModuleA引用了Prism.Wpf;主工程引用了Prism.unity;ModuleA两个工程,主工程通过引用项目的方式引入MouduleA,然后再通过重写PrismApplication的ConfigureModuleCatalog()加载了ModuleA下的ModuleAModule类。ModuleAModule类实现了IModule的OnInitialized()方法,并再该方法关联区域和显示视图,用于显示内容。这种加载方式感觉会比第一种好一些,我们继续往下看下一个例子。
观察07-Modules-Directory示例
分为ModuleA和Modules两个工程,ModuleA工程引用了Prism.Wpf、Modules工程引用了Prism.Unity;
1、ModuleA工程
ModuleA工程引用了Prism.Wpf;
1.1、新建了ModuleAModule.cs类
ModuleAModule继承自Prism.Modularity.IModule,并实现了OnInitialized()、RegisterTypes()
OnInitialized()方法同样使用传入的IContainerProvider容器代理对象Resolve解析出IRegionManager,然后使用这个RegionManager对象去关联一个区域和视图。
1.2、Views下的ViewA.xaml
和其他工程一样,包含一个显示控件显示一个ViewA 字号为38;cs文件中无新增代码。
2、Modules工程
Modules工程引用了Prism.Unity;
2.1、App.xaml
新增命名控件xmlns:prism="http://prismlibrary.com/"
修改Application为PrismApplication;
去掉StartupUri属性
2.2、App.cs
修改App继承自PrismApplication;
重写CreateShell()使用Container.Resolve()解析MainWindow用作启动窗体;
重写RegisterTypes() 不重写编译失败。
重写CreateModuleCatalog()方法
protected override IModuleCatalog CreateModuleCatalog()
{
return new DirectoryModuleCatalog() { ModulePath = @".\Modules" };
}
实例化了DirectoryModuleCatalog并传入了路径.\Modules,因为是从目录加载的,这里不知道是不是固定写法,然后所有自己创建的Module只要继承自IModule就行吗?这里没有验证,只是学习知道了可以从目录加载。继续往下。
3.1、Views下的MainWindow.xaml
包含了一个显示控件同时设置了附加依赖项属性区域名称ContentRegion。cs文件无新增代码。
4、先编译ModuleA、然后再运行Modules程序,不然报错;
界面显示View A
总结:使用Directory加载Module的话,再主工程中不需要引用各个Module,只需要配置目录的路径即可,但是没有验证新增ModuleB、C等等,是否可以自动加载进来,这个可以结合第一篇的代码,使用Resolve资源配合创建按钮,再各个Module中Activate和Deactivate修改主工程中区域名称对应的显示内容。
观察07-Modules - LoadManual示例
包含ModuleA和Modules两个工程;ModuleA引用了Prism.Wpf包;Modules引用了Prism.Unity包;
1、ModuleA工程
ModuleA工程只引用了Prism.Wpf;
1.1、新增ModuleAModule.cs
ModuleAModule继承自Prism.Modularity.IModule,并实现了OnInitialized()和RegisterTypes()接口,OnInitialized()方法和前面的项目一样,同样使用IContainerProvider容器代理Resolve解析RegionManager()对象,然后使用regionManager关联区域名称和对应的视图,用于显示。
1.2、Views下的ViewA.xaml
包含用于显示的TextBlock控件。显示内容为View A ,字号为38,cs文件中无修改;
2、Modules工程
Modules工程引用了Prism.Unity;和ModuleA项目;
2.1、App.xaml
增加命名空间prism="http://prismlibrary.com/"
修改Application为PrismApplication
去掉StartupUri属性
2.2App.cs
修改App继承自PrismApplication;
重写CreateShell()方法;
使用Container.Resolve()方法设置启动窗口为MainWindow。
重写RegisterTypes()方法,不重写编译报错。
重写ConfigureModuleCatalog()方法
protected override void ConfigureModuleCatalog(IModuleCatalog moduleCatalog)
{
var moduleAType = typeof(ModuleAModule);
moduleCatalog.AddModule(new ModuleInfo()
{
ModuleName = moduleAType.Name,
ModuleType = moduleAType.AssemblyQualifiedName,
InitializationMode = InitializationMode.OnDemand
});
}
再添加ModuleA的工程引用后,直接使用typeof读取ModuleAModule。然后再moduleCatalog中使用AddModule方法,新建一个ModuleInfo()对象,包含了,Module的名称、限定名称、加载方式为按需加载;
2.3、Views下的MainWindow.xaml
设置了一个ContentControl 显示控件,并设置了附加依赖项属性用于关联显示区域ContentRegion。
添加了一个Button,注册了Click事件;
2.4、Views下的MainWindow.cs
我们来到CS文件中,我们看到了构造函数中初始化 IModuleManager对象,然后再用户点击Button的时候,使用LoadModule()方法加载ModuleAModule。
3、先编译ModuleA,再运行Modules
我们看到启动后,显示Load Module的Button,点击这个Button后显示View A文字。
总结:再Modules工程中,引入了Prism.Unity和ModuleA,再ModuleA中引用Prism.Wpf。
想使用LoadManual加载的方式,在App.cs中重写ConfiureModuleCatalog()方法时,获取typeof对应的ModuleA工程下的类对象,然后配置对应的程序集信息,在需要的地方,使用_moduleManager下的LoadModule()方法去加载对应的Module,各个Module去实现自己的IModule,用于在OnInitialized()中关联区域和视图。
观察07-Modules - Xaml示例
包含ModuleA工程和Modules工程两个工程,ModuleA引用了Prism.Wpf包、Modules引用了Prism.Unity包和ModuleA项目;
1、ModuleA工程
添加了对Prism.Wpf包的引用;
1.1、创建了ModuleAModule.cs类,继承了Prism.Modularity.IModule接口,实现了OnInitialized()和RegisterTypes()方法,并在OnInitialized()方法中使用传入的IContainerProvider对象调用Resolve发方法解析IRegionManager对象。然后使用IRegionManager的实例来关联字符串为ContentRegion和ViewA视图,用于显示。
1.2、Views下创建了用户自定义控件ViewA.xaml
里面有一个用户显示内容的TextBlock控件,显示内容为View A字号为38,cs文件中无新增内容
2、Modules工程
Modules引用了Prism.Unity包和ModuleA工程;
2.1、App.config
在App.config中添加了configSections节点,里面配置了Prism.Modularity.ModulesConfigurationSection和Modules节点,
用于添加引用Prism下的库,和加载ModuleA.dll的配置项;
2.2、App.xaml
添加了命名空间xmlns:Prism:="http://prismlibrary.com"
修改Application为PrismApplication
去掉StartupUri属性
2.3App.cs
修改App继承自PrismApplication
重写CreateShell()方法,使用Container.Resolve解析MainWindow,并返回做为启动窗体。
重写RegisterTypes()
重写CreateModuleCatalog()
使用XamlModuleCatalog()方法,传入URI。URI文本是当前工程的ModuleCatalog.xaml资源文件作为IModuleCatalog。
2.3ModuleCatalog.xaml
添加命名空间xmlns:m="clr-namespace:Prism.Modularity;assembly=Prism.Wpf"
修改根节点为m:ModuleCatalog,添加子节点m:ModuleInfo.并包含了ModuleA工程的信息。
<m:ModuleCatalog xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:m="clr-namespace:Prism.Modularity;assembly=Prism.Wpf">
<m:ModuleInfo ModuleName="ModuleAModule"
ModuleType="ModuleA.ModuleAModule, ModuleA, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
</m:ModuleCatalog>
2.4Views下的MainWindow.xaml
包含了一个用于显示的ContentControl控件,添加了附加依赖项属性RegionName。cs代码无新增
先编译ModuleA,在运行Modules
我们看到了界面显示View A
总结:ModuleA工程引用Prism.Wpf包,通过继承自Prism.Modularity.IModule接口,并重写OnInitialized()方法获取RegionManager的对象,用来在ModuleA工程中关联显示区域Region和视图;在主工程Modules中添加Prism.Unity和ModuleA工程;
App.cs中重写CreateModuleCatalog()方法,并创建XamlModuleCatalog对象关联创建的ModuleCatalog.xaml资源文件,同时App.config中也引用ModuleA.dll配置。
ModuleCatalog.xaml资源文件设置ModuleCatalog和ModuleInfo来管理引用的Module工程。我尝试了删除App.config内容和工程,发现会报错。所以必须包含以上内容。
我们打开WPFPrismDemo工程,挑选前面5种加载方式的其中一种,写自己的加载代码。
我选择了07-Modules - Code这个示例用来加载代码;
我选择的原因是ModuleA,还是解耦后独立出来的工程,和主工程的关联关系就是主工程下引用,然后再App.cs下通过重写configureModuleCatalog()方法,加载对应的Module模块。关联关系就创建了。没有额外的操作,也不需要完整加载目录。不要参考前面的代码,如果忘记了,可以上去看一下,回忆一下你选择的加载方式,然后我们开始写:
再WPFPrismDemo工程上,新建一个类库工程ModuleSalesForecast模块,是我们的销售预测模块,用于解耦销售数据显示和销售预测显示根其他模块的重叠,主要是报表功能。
ModuleSalesForecast引用Prism.Wpf;
新建ModuleSalesForecastModule类,并继承Prism.Modularity.IModule接口.
重写OnInitialized()方法时关联SalesForecastRegion(还未定义)区域和创建的Views下的ViewSalesForecast.xaml(还未定义)。
using ModuleSalesForecast.Views;
using Prism.Ioc;
using Prism.Modularity;
using Prism.Regions;
namespace ModuleSalesForecast
{
public class ModuleSalesForecastModule : IModule
{
public void OnInitialized(IContainerProvider containerProvider)
{
var regionManager = containerProvider.Resolve<IRegionManager>();
regionManager.RegisterViewWithRegion("SalesForecastRegion", typeof(ViewSalesForecast));
}
public void RegisterTypes(IContainerRegistry containerRegistry)
{
}
}
}
新建Views文件夹
新建ViewSalesForecast.xaml
目前就放置一个TextBlock,显示为预测今年销售额全是第二。大小等于38;
ModuleSalesForecast工程部分我们目前就完成了
接下来是WPFPrismDemo部分
再WPFPrismDemo工程引用ModuleSalesForecast工程
打开App.cs
重写ConfigureModuleCatalog()方法
并调用AddModule方法,解析ModuleSalesForecastModule;
using Prism.DryIoc;using Prism.Ioc;using Prism.Modularity;using System.Windows;namespace WPFPrismDemo{ /// <summary> /// App.xaml 的交互逻辑 /// </summary> public partial class App : PrismApplication { protected override Window CreateShell() { return Container.Resolve<MainWindow>(); } //这个方法如果不重写则会编译报错。 protected override void RegisterTypes(IContainerRegistry containerRegistry) { } protected override void ConfigureModuleCatalog(IModuleCatalog moduleCatalog) { base.ConfigureModuleCatalog(moduleCatalog); moduleCatalog.AddModule<ModuleSalesForecast.ModuleSalesForecastModule>(); } }}
在主窗体的MainWindow中,我们基于上一个例子,修改代码如下,增加用于显示的ContentControl ,并设置附加依赖项属性RegionName为SalesForecastRegion。
<Window x:Class="WPFPrismDemo.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:prism="http://prismlibrary.com/" xmlns:local="clr-namespace:WPFPrismDemo" mc:Ignorable="d" Title="MainWindow" Height="450" Width="800"> <DockPanel > <StackPanel> <Button Content="Activate View A" Click="ActivateViewA_Click"/> <Button Content="Deactivate View A" Click="DeactivateViewA_Click"/> <Button Content="Activate View B" Click="ActivateViewB_Click"/> <Button Content="Deactivate View B" Click="DeactivateViewB_Click"/> </StackPanel> <ContentControl prism:RegionManager.RegionName="ContentRegion" FontSize="30" VerticalAlignment="Center" HorizontalAlignment="Center"/> <ContentControl prism:RegionManager.RegionName="SalesForecastRegion"/> </DockPanel></Window>
运行一下我们的代码:观察结果
依次是StackPanel中的按钮,和ViewA和ViewB切换的视图,和预测分析的视图。从这一篇Modules不同的加载方式就梳理完了。
继续往后学习,07示例结束了,还有22个示例没有学习;