二、从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和主工程的引用关系。

二、从GitHub浏览Prism示例代码的方式入门WPF下的Prism之Modules的几种加载方式

观察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>

运行一下我们的代码:观察结果

二、从GitHub浏览Prism示例代码的方式入门WPF下的Prism之Modules的几种加载方式

依次是StackPanel中的按钮,和ViewA和ViewB切换的视图,和预测分析的视图。从这一篇Modules不同的加载方式就梳理完了。

继续往后学习,07示例结束了,还有22个示例没有学习;

我创建了一个C#相关的交流群。用于分享学习资料和讨论问题。欢迎有兴趣的小伙伴:QQ群:542633085

二、从GitHub浏览Prism示例代码的方式入门WPF下的Prism之Modules的几种加载方式

上一篇:C# 简单编程入门


下一篇:Windows常用快捷键