這一篇梳理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個示例沒有學習;