我看prism的一点感受(一) 从Bootstrapper说起
我是从09年开始接触Silverlight,当时是2.0版. 学习了一个月后,正好接到一个单子,就想用Silverlight来做前端界面.也是晕天暗地的一边做一边学.感谢园子里的老师们,我就是一边看他们的博客一边做项目的,其中特别感谢TerrLee,他的这篇文章<一步一步学silverlight 2 入门>带我进入了silverlight的世界.
当后来我把这个项目部署到服务器上给客户使用时,出现了一个问题.我这个项目的Xap包达到了6M,有些客户在进入主界面时,居然要等几分钟才能下载完Xap包,当然是抱怨连天了,赶紧的解决的办法,后来在silverlight帮助文档里找到了关于按需下载的说明.照着它,我分割了项目,这样也算免强过关了.
在项目完工后,跟朋友聚会时,听说了有一个叫Prism的架框,可以用于silverlight的开发,具体如何当时也没有听懂.直到上个月,决定把去年做的那个项目升级,并把silverlight版本定为4.0版,想到了Prism这个框架,并想把这个框架应用到升级版。才开始了学习Prism的路程,这其中也少不了看园子里其他老师的博客。
就是带我了解Prism的文章。
当然,在学习这些文章的同时,我也把Prism Library的源码看了几遍,对其框架和意路也有自已的了解。
为了不忘记我所学的这些知识,特地把学习的笔记抄录下来,放在博客里,可能过几年我再用到这个东西时,可以拿出来温故一下 。
=====================================================================================
Prism 是一个复合应用程序的框架。
上面一句话里其实包含着三个信息,我们一个个来解释。
1. Prism是嘛东西?
我查过google, google对此单词的解释是“棱镜”。哦,原来它是一面镜子,那原来上面一句话就可以说成是”镜子是一个复合应用程序的 框架”,其实这是不对的,微软对它的解释是” Composite Application Guidance for WPF and
Silverlight.” 也就 是WPF和Silverlight的复合应用程序指南,最新的Prism 4版,也包括了对Windows Phone 7
应用程序的复合应用支持
2.什么是复合应用程序
我猜想就是把一个程序拆分成几个程序,然后运行时再一个个拼凑起来,就成了复合程序了。
3.框架
把我们经常要用的类重新包装一遍,各个类都有互相调用,我们在写代码时调用这些类会少写很多代码?这就叫框架??
从 bootstrapper 说起:
用过silverlight开发的朋友都知道,silverlight程序的启动是从App类的Application_Startup方法开始,这个方法里有一句代码
private void Application_Startup(object sender, StartupEventArgs e)
{
this. RootVisual = new MainPage();
}
上面这段代码,就是程序启动后,实例化sliverlight项目中的MainPage这个类,再赋值给应用程序类(App)的RootVisual属性,这样我们第一个看到的窗口就是MainPage页面了,MainPage一般是我们在Silverlight程序里定义第一个显示的页面。这段代码的功能就是让程序在启后,立马显示MainPage页面。
但是使用Prism框架后,这段代码就发生了改变,程序启动后首先去实例化MyBootstrapper类,然后调用MyBootstrapper的方法Run();
private void
Application_Startup(object sender, StartupEventArgs e)
{
MyBootstrapper
boot = new MyBootstrapper();
boot.Run();
}
现在这一改,就看不懂什么意思了,怎么没有要显示的页面???MyBootstrapper是什么类,Run方法又在干什么??
坛子里有大师出来指点我们,MyBootstrapper是使用Prism框架要定义的第一个类,它是从Prism框架的UntiyBootstrapper类继承而来,我们需要重载这个基类的几个方法,就可以显示出界面来了。
好,现在终于接触到Prism里面的东西了,那我们一起来看看UntiyBootstrapper到底是个什么类,它是干什么用的??
Untiybootstrapper类就定义在Prism.UntiyExtensions.Silverlight项目的UntiyBootstrapper.cs文件里,现在我们打开这个文件,看看里面的代码。
呵呵,这个类的命名空间是Microsoft.Practice.Prism.UnityExtensions, 它继承于Bootstrapper类,又有一个基类,那里面的代码也不看了,接着找,看看Bootstrapper类是干什么的,在Prism.Silverlight项目里有个Bootstrapper.cs文件,里面就是Bootstrapper类的定义。
现在大家跟我一起进入到这个文件里一起看看。这个文件代码比较多,截图看不到完整的,我就给大家讲讲吧,这里面有些什么
{
Protected ILoggerFacade Logger { get; set; } //保护属性,是一个专门用来写日志的类
Protected IModuleCatalog ModuleCatalog { get; set; } //模块目录,保护属性,是用来保存程序中所有要用到的模块信息
Protected DependencyObject Shell { get; set; } //Shell,一个依赖对象
以上三个属性具体是可以干什么,我们在后面列专题来说,这里大家只要知道这个类有这三个属性就可以了
protected virtual ILoggerFacade CreateLogger() //虚方法,用来创建LoggerFacade类的对象
{
//如果项目是silverlight的,就创建EmptyLogger对象,如果是WPF的,就创建TextLogger对象,它们都是ILoggerFacade接口的子类
#if SILVERLIGHT
return new EmptyLogger();
#else
return new TextLogger();
#endif
}
public void Run() //这是这个类的两个公共方法之一,我们在上面的示例代码中MyBootstrapper就是调用这个方法
{ this.Run(true);}
public abstract void Run(bool runWithDefaultConfiguration); //抽象方法,等着人继承咧
protected virtual IModuleCatalog CreateModuleCatalog() //创建一个模块目录类的实例
{ return new ModuleCatalog(); }
protected virtual void ConfigureModuleCatalog(){} //虚方法,配置模块目录
protected virtual void RegisterFrameworkExceptionTypes() //虚方法,注册一个异常类型
{
ExceptionExtensions.RegisterFrameworkExceptionType(
typeof(Microsoft.Practices.ServiceLocation.ActivationException));
}
protected virtual void InitializeModules() //初始化模块,这里创建一个模块管理器接口的实例
{
IModuleManager manager = ServiceLocator.Current.GetInstance<IModuleManager>();
manager.Run();
}
//配置区域适配器映射,其实就是在这里事先申请哪些类型的控件可以做其它模块的容器,Silverlight可以使用四种控件来做为模块的容器,什么是区域,后面我们来解释
protected virtual RegionAdapterMappings ConfigureRegionAdapterMappings()
{
RegionAdapterMappings regionAdapterMappings = ServiceLocator.Current.GetInstance<RegionAdapterMappings>();
if (regionAdapterMappings != null)
{
#if SILVERLIGHT
regionAdapterMappings.RegisterMapping(typeof(TabControl), ServiceLocator.Current.GetInstance<TabControlRegionAdapter>());
#endif
regionAdapterMappings.RegisterMapping(typeof(Selector), ServiceLocator.Current.GetInstance<SelectorRegionAdapter>());
regionAdapterMappings.RegisterMapping(typeof(ItemsControl), ServiceLocator.Current.GetInstance<ItemsControlRegionAdapter>());
regionAdapterMappings.RegisterMapping(typeof(ContentControl), ServiceLocator.Current.GetInstance<ContentControlRegionAdapter>());
}
return regionAdapterMappings;
}
//配置区域的行为
protected virtual IRegionBehaviorFactory ConfigureDefaultRegionBehaviors()
{
var defaultRegionBehaviorTypesDictionary = ServiceLocator.Current.GetInstance<IRegionBehaviorFactory>();
if (defaultRegionBehaviorTypesDictionary != null)
{
defaultRegionBehaviorTypesDictionary.AddIfMissing(AutoPopulateRegionBehavior.BehaviorKey,
typeof(AutoPopulateRegionBehavior));
defaultRegionBehaviorTypesDictionary.AddIfMissing(BindRegionContextToDependencyObjectBehavior.BehaviorKey,
typeof(BindRegionContextToDependencyObjectBehavior));
defaultRegionBehaviorTypesDictionary.AddIfMissing(RegionActiveAwareBehavior.BehaviorKey,
typeof(RegionActiveAwareBehavior));
defaultRegionBehaviorTypesDictionary.AddIfMissing(SyncRegionContextWithHostBehavior.BehaviorKey,
typeof(SyncRegionContextWithHostBehavior));
defaultRegionBehaviorTypesDictionary.AddIfMissing(RegionManagerRegistrationBehavior.BehaviorKey,
typeof(RegionManagerRegistrationBehavior));
defaultRegionBehaviorTypesDictionary.AddIfMissing(RegionMemberLifetimeBehavior.BehaviorKey,
typeof(RegionMemberLifetimeBehavior));
}
return defaultRegionBehaviorTypesDictionary;
}
//创建Shell
protected abstract DependencyObject CreateShell();
//初始化Shell
protected virtual void InitializeShell();
//配置服务定位类
protected abstract void ConfigureServiceLocator();
}
看了上面这些代码,估计不少人会头晕眼花,不知道这是干嘛的,其实我们不必考虑那么多,因为这个类是个抽象类,它始终是要被子类继承的,作为子类最关心的是,我能继承些什么下来,需要重载些什么方法
所以我们只需要知道几点就行:
1.
这个类里有些什么东西(主要是属性)
2.
这个类里做了些什么(哪些方法是实现了,子类是可以不用重载的)
3.
这个类没有做什么(哪些方法没有实现,子类需要重载)
下面我就来回答
1 该类里面定义了三个属性Logger, ModuleCatalog, Shell。这三个属性都是受保护的成员,只能在类的内部和子类的内部使用
logger 是用来写日志,prism 的每一步操作都会记录下来
ModuleCatalog 见名思意是模块目录的意思,其实它里面就是一个列表,把项目中要加载的每个模块的信息给保存起来。当初始运行prism时,ModuleManager这个类的实例会读取ModuleCatalog里面保存的所有模块的信息(比方说,是哪个xap文件,里面模块命名空间是什么,模块的类型名是什么,这个模块是否依赖于其他的模块,这模是否在程序初始化时就下载,还是留在以后程序运行的过程中,想下载的时候再下载)
Shell 是一个依赖对象类型,其实它在后面代码中的作用是保存Mainpage的引用
2 类代码里定义了实例化Logger,ModuleCatalog两个属性的方法,也就是说,只要子类调用这两个方法,就可以创 建Logger,ModuleCatalog两个属性的对象,可以拿着用了.
实现了区域控件的定义,区域行为的定义,和创建IModuleManager接口的实现代码
3 没有做的事,这是我们最关心的,它没有实现,那么子类就要去实现他。
没有实现的有
public abstract Run(Bool) 方法
virtual void ConfigureModuleCatalog(){}
protected abstract DependencyObject
CreateShell();
protected virtual void InitializeShell()
protected abstract void ConfigureServiceLocator();
所以等会我们看UntiyBootstrapper类时,一定要关注
{
private bool useDefaultConfiguration = true; //是否使用默认的配置
public IUnityContainer Container { get; protected set; } //容器属性,这个容器是用来实例化对象的。具体怎么实例化后面再来讲
public override void Run(bool runWithDefaultConfiguration) //呵呵,一上来就实现父类的Run(bool) 方法
{
this.useDefaultConfiguration = runWithDefaultConfiguration;
this.Logger = this.CreateLogger(); //创建日志类实例,这是调用了父类的方法
this.ModuleCatalog = this.CreateModuleCatalog(); //创建模块目录类实例,这是调用父类的方法
//Logger, ModuleCatalog就是父类里面三个属性之二。
this.ConfigureModuleCatalog(); //配置模块目录,这是父类里没有实现的虚方法,在这个类里也没有实现,等着下一个子类去实现了
this.Container = this.CreateContainer(); //实例化依赖注入容器类,这是本类的方法,实现了
this.ConfigureContainer(); //配置容器,这是本类的方法,实现了
this.ConfigureServiceLocator(); //配置服务定位,这是本类的方法,实现了
this.ConfigureRegionAdapterMappings(); //配置区域适配器映射,这是调用父类的方法,在父类里已实现
this.ConfigureDefaultRegionBehaviors(); //配置区域行为,这是调用父类的方法,在父类里已实现
this.RegisterFrameworkExceptionTypes();//注册框架异常类型,这是在本类里重载了父类已实现的方法
this.Shell = this.CreateShell(); //创建Shell视图,这是父类没有实现的方法,在本类也没有实现,得等着它的子类去实现了
if (this.Shell != null)
{
RegionManager.SetRegionManager(this.Shell, this.Container.Resolve<IRegionManager>()); //设置区域管理器
RegionManager.UpdateRegions();//区域管理器更新区域
this.InitializeShell();//初始化Shell,这是父类里没有实现地方法,本类也没有实现
}
if (this.Container.IsRegistered<IModuleManager>())
{
this.InitializeModules(); //初始化模块,就是创建一个modulemanager管理器的对象
}
}
这里呢,笔者偷了个懒,UntiyBootstrapper类的其他方法没有列出来,只列出了Run()方法,一来是该类其他的方法都是在Run里被调 用,等会笔者在介绍Run方法,也会详细介绍其他方法的,二来我还是最关心的是上面用红色标注释的方法,这些得在子类里实现呀,其他的方法就暂时不管了。
好了,介绍到这里,Prism框架里面首当其中的两个类Bootstrapper, UntiyBootstrapper都出场了,现在接下来该我们来写代码了。
按Prism的文档介绍,在我们的应用程序里首先要继承UntiyBootstrapper类,实现其中几个方法的重载,用来加载我们的应用程序中的模块和指定初始显示的视图。继承是一定要的,那么重载哪个方法咧,就是上面用红色标注的几个方法,接下来看代码
{
//重载父类的方法,加载一个模块,并返回ModuleCatalog对象
protected override IModuleCatalog CreateModuleCatalog()
{
return Microsoft.Practices.Prism.Modularity.ModuleCatalog.CreateFromXaml(
new Uri(“模块名”, UriKind.Relative));
}
//重载父类的方法,创建一个MainPage的实例,看到这里,会心一笑,原先在Application_Startup(object sender, StartupEventArgs e)方法里面实例化MainPage移到这里来了
protected override DependencyObject CreateShell()
{
MainPage view = this.Container.TryResolve<MainPage>();
return view;
}
protected override void InitializeShell()
{//给应用程序的RootVisual 属性赋值,这也是Application_Startup(object sender, StartupEventArgs e)方法的原始代码
Application.Current.RootVisual = Shell;
}
}
看到这里,我们再回过头来,看看文章开始的示例代码,再一起回味一下MyBootstrapper类的执行过程。
原先的代码
private void Application_Startup(object
sender, StartupEventArgs e)
{
this.
RootVisual = new MainPage();
}
在整个应用程序启动后,会触发App.Startup事件,并调用Application_Startup方法,在这个方法里,我们首先实例化一个MainPage类,这个类就是我们首先要显示的Xaml文件,接下来把这个实例引用赋值给this.RootVisual,这样在程序启动后,就会显示MainPage页面了。
现在我改成这样的代码
private void
Application_Startup(object sender, StartupEventArgs e)
{
MyBootstrapper
boot = new MyBootstrapper();
boot.Run();
}
那个这个代码会怎么做呢,它首先会实例化MyBootstrapper的父类的父类Bootstrapper类,在栈里分配了三个属性的引用变量的空间,也就是
Logger
ModuleCatalog
Shell
这三个类型的引用。
接下来,实例化它的父类UntiyBootstrapper,为UntiyBootstrapper的两个属性成员分配空间,
bool useDefaultConfiguration 值变型变量
IUnityContainer Container 依赖注入容器对象的引用。
最后就是调用boot.Run()这个方法了,而这个方法又是调用Run(bool)
方法,所以我们还是再看看这个方法里面的代码是什么执行的了。
第一步 this.Logger = this.CreateLogger(); //创建日志类实例,这是调用了Bootstrapper类的方法
第二步 this.ModuleCatalog
= this.CreateModuleCatalog(); //创建模块目录类实例,这个方法在MyBootstrapper重载,把一个具体的模块加入到ModuleCatalog对象里,并返回ModuleCatalog对象的引用
第三步 this.ConfigureModuleCatalog();
这个方法在所有的类里都没去实现它,但也不影响程序运程
第四步 this.Container = this.CreateContainer(); //实例化依赖注入容器类,这是调用了UntiyBootstrapper 类的方法实例化Container
第五步 this.ConfigureContainer(); //配置容器,这是调用了UntiyBootstrapper 类的方法实现
第六步this.ConfigureServiceLocator(); //配置服务定位,这是调用了UntiyBootstrapper 类的方法实现
第七步 this.ConfigureRegionAdapterMappings(); //配置区域适配器映射,这是调用Bootstrapper的方法,在Bootstrapper类里实现指定哪些控件可以作为模块的容器
第八步
this.ConfigureDefaultRegionBehaviors();
//配置区域行为,这是调用Bootstrapper的方法,在Bootstrapper类里已实现
第九步 this.Shell
= this.CreateShell(); //这个很重要,在MyBootstrapper类里实现,就是创建一个MainPage页面的对象,并把这个实例的引用赋值给Shell属性(Shell是在Bootstrapper类里定义的)
if (this.Shell != null)
{
RegionManager.SetRegionManager(this.Shell,
this.Container.Resolve<IRegionManager>()); //把视图和区域关联起来
RegionManager.UpdateRegions();//区域管理器更新区域,其实是触发一个事件,通知框架区域已设置好
this.InitializeShell();//初始化Shell,这是在MyBootstrapper类里实现,就是把MainPage设置为首先显示的页面
}
if
(this.Container.IsRegistered<IModuleManager>())
{
这是第十步 this.InitializeModules(); //初始化模块,就是创建一个modulemanager管理器的对象
}