我看prism的一点感受(三) 初始化模块
上一篇讲到了模块是如何下载,以及加载到宿主程序中.但是在下载之前,我们要指定模块的路径,以及模块的类型名,才能有效下载,以及加载.
在第一篇的UnityBootstrapper类的讲述中,我们提到了要继承这个类,并重载几个方法.
其中一个重载的方法就是
protected override IModuleCatalog CreateModuleCatalog()
return Microsoft.Practices.Prism.Modularity.ModuleCatalog.CreateFromXaml(
new Uri(“模块名”, UriKind.Relative));
}
这个方法里有一行代码,是调用ModuleCatalog.CreateFromXaml方法,这个方法的作用是读取模块配置文件里面的内容,并产生ModuleInfo对象或ModuleInfoGroup对象,并将这对象放入到MdouleCatalog类里.
方法有两个参数,第一个参数我在第一篇里写错了,不应该是”模块名”, 而应该是”模块配置文件名”,这里做个纠正. 第二个参数是指第一参数给的文件路径Uri 写的是绝对路径或是相对路径.
接下来我按”配置文件种类和格式”-------à”ModuleInfo和MduleInfoGroup”-àModuleCatalog这个顺序来讲
一般认为写在silverlight资源文件里比较好,这是一个以xaml为后缀的文件名.书写的格式如下:
<prism:ModuleCatalog
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns: prism="clr-namespace:Microsoft.Practices.Prism.Modularity;assembly=Microsoft.Practices.Prism">
<prism:ModuleInfo Ref="RentCar.LocationService.xap"
ModuleName="RentCar.LocationService"
ModuleType="RentCar.LocationService.ModuleInit,RentCar.LocationService,Version=1.0.0.0"
InitializationMode="WhenAvailable" />
</prism:ModuleCatalog>
首先以Modulearity:ModuleCatalog节点开头, Modulearity是命名空间名,这在文件的第四行可以看到定义, ModuleCatalog是根节点名.这个好象不能写错
接下来可以看里面有一个ModuleInfo节点, 这表示整个项目里只有一个模块要下载,其中属性Ref 是要下载的xap文件的路径.可以是相对路径也可以是绝对路径.
ModuleName是项目的名称.
ModuleType 是一个由逗号分割的字符串组成,第一部分是指定加载模块后,要实例化的第一个类名.
第二部分指这个类的命名空间
第三部分是模块的版本号(可不写)
第四部分是模块的文化语言代码(可不写)
第五部指模块的公匙标记(可不写)
InitializationMode
是即时下载还是以后下载, 这个值在"WhenAvailable"和”OnDemand”中二选一
注:配置文件中还有其他的节点,比方moduleinfogroup
, modulestate,依赖模块等等,这里就不介绍了,可以查阅Prism的帮助文档
…….
现在我们来看看CreateFromXaml方法是如何读取Xaml文件的.
{
if (xamlStream == null)
{
throw new ArgumentNullException("xamlStream");
}
#if SILVERLIGHT
string xaml;
using (System.IO.StreamReader reader = new System.IO.StreamReader(xamlStream))
{
xaml = reader.ReadToEnd();
}
return XamlReader.Load(xaml) as ModuleCatalog;
#else
return XamlReader.Load(xamlStream) as ModuleCatalog;
#endif
}
这个方法里调用了Silverlight类库中XamlReader.Load方法.
XamlReader.Load方法是”分析格式良好的 XAML 片段并创建相应的 Silverlight 对象树,然后返回该对象树的根”.
这是什么意思咧,XAML按照根节点,子节点,孙子节点等格式定义好,XamlReader.Load读取这个文件后,可以按这个格式产生一个对象树,也就是一个对象包含一个子对象,子对象又包含子对象这样的一个情况.
那么在上面的代码里,XamlReader.Load方法读取了文件流后,产生了对象,并把根节点转换成ModuleCatalog对象.再把子节点ModuleInfo也生成一个对象,并放入到一个Itmes属性中,Items是Collection类型的 .
注意: 1. 看到这里我们应该明的了,MoudleCatalog其实就是包装一个集合类的,这个集类可以存入moduleInfo 和 modulegroup两个类型的对象.
有兴趣的同学可以看看items 这个属性是继承了什么接口,而moduleInfo和modulegroup又是继承了什么接口,从而使他们产生关联.
注意:2. ModuleCatalog其实也可以不读xaml文件,而是直接调用AddModule和AddModuleGroup两个方法,把moduleInfo和moduleGroup两种类型加载到Items里面去
注意3, 既然能将模块信息添加到ModuleCatalog类里去,当然也提供了从ModuleCatalog读取模块信息的方法,但是,,,但是,,,但是,,,没有提供从ModuleCatalog里的集合中删除模块信息的方法!!!!!!!!如果你愿意,是可以在源代码中去添加这个方法的.
我们综合前一篇文章,可以大致明白Prism中几个类的分工,ModuleCatalog是用来存储我们项目中所有要下载的模块相关信息(注意是模块相关信息,不是模块本身),而ModuleManager类,则从ModuleCatalog中读取这些信息,去下载指定的模块.
{
IEnumerable<ModuleInfo> module = this.moduleCatalog.Modules.Where(m => m.ModuleName == moduleName);
if (module == null || module.Count() != 1)
{
throw new ModuleNotFoundException(moduleName, string.Format(CultureInfo.CurrentCulture, Resources.ModuleNotFound, moduleName));
}
IEnumerable<ModuleInfo> modulesToLoad = this.moduleCatalog.CompleteListWithDependencies(module);
this.LoadModuleTypes(modulesToLoad);
}
LoadModule方法就是根据参数提供的模块名,这应该就是模块项目名称.到moudleCatalog.Modules集合中找到对应的ModuleInfo类,然后根据ModuleInfo类中的文件路径去下载了.
现在我们知道了Prism是怎么保存模块信息,以级怎么下载模块,加载模块到宿主进程.但是下载来的模块总要显示视图出来吧,那视图怎么显示咧????
通常我们在功能模块里总要写上这一个类{
private readonly IUnityContainer container;
private readonly IRegionManager regionmanager;
public ModuleInit(IUnityContainer container,IRegionManager regionmanager)
{
this.container = container;
this.regionmanager = regionmanager;
}
public void Initialize()
{
this.regionmanager.RegisterViewWithRegion("MainRegion",()=>this.container.Resolve<ModulePage>());
}
}
这个类是每个模块必须要写的,它主要是告诉Prism框架,我把我下载后,把我的视图放在哪个区域里(关于区域Region,是下一篇要讲的内容)
现在讲讲这类里面的两个方法,一是构造函数,从外面接收一个依赖注入容器( 这个类从来没讲过,这里简单讲讲,它的作用就是帮我们实例化对象,不需要我们去new一个对象出来)
再就是接收一个区域管理器,这个区域管理器就是控制视图加载到区域里面 的.
第二个函数名叫Initialize()这个函数名一定不能写错,一定要这样
这个函数的功能就是实例化一个模块中的视图
this.container.Resolve< ModulePage >()
然后把这个视图嵌入到“容器视图”的某个区域中去。这个打引号的容器视图就是我们在第一篇中讲到的MainPage视图,它是整个项目的主视图。其他功能模块的视图全嵌在这个视图里面。
嗯!!!
这个类是怎么被调用的????
还是按步骤来。
第一步, 我们写个xaml的模块配置文件,这个文件中ModuleInfo节点有个属性ModuleType,还记得不???看上面!!!!!
这个ModuleType的值,第一段就写了
ModuleType="RentCar.LocationService.ModuleInit
这是Prism框架下载模块后,第一个要实例化的类。
第二步,由ModuleCatalog去读这个文件,把ModuleInfo节点转化成对象存储在集合里
第三步,由ModuleManager读取ModuleCatalog中的信息,去下载模块。
public void Initialize()
{
this.regionmanager.RegisterViewWithRegion("MainRegion",()=>this.container.Resolve<ModulePage>());
}
方法,加载视图到区域。
那这个活由谁来干??
ModuleInitializer 类。
一起来看看ModuleManger.LoadModuleTypes是怎么干的。
该方法在循环中调用this.BeginRetrievingModule(moduleInfo);去下载模块。
在这个方法的最后一行this.LoadModulesThatAreReadyForLoad() 方法里就是去实例化ModuleInit类了。
protected void LoadModulesThatAreReadyForLoad()
{
bool
keepLoading = true;
while
(keepLoading)
{
keepLoading = false;
IEnumerable<ModuleInfo> availableModules = this.moduleCatalog.Modules.Where(m => m.State == ModuleState.ReadyForInitialization);
foreach
(ModuleInfo moduleInfo in availableModules)
{
{
moduleInfo.State = ModuleState.Initializing;
this.InitializeModule(moduleInfo);
keepLoading = true;
break;
}
}
}
}
这个方法是在下载模块以后,找出没有初始化模块,对其初始化。
this.InitializeModule(moduleInfo); 这是倒数第三行代码。
private void InitializeModule(ModuleInfo
moduleInfo)
{
if
(moduleInfo.State == ModuleState.Initializing)
{
this.moduleInitializer.Initialize(moduleInfo);
moduleInfo.State = ModuleState.Initialized;
this.RaiseLoadModuleCompleted(moduleInfo,
null);
}
}
看到标成红色的代码没有,这个this.moduleInitalizer 就是ModuleInitializer 类的实例