我看prism的一点感受(一) 从Bootstrapper说起

       我是从09年开始接触Silverlight,当时是2.0版. 学习了一个月后,正好接到一个单子,就想用Silverlight来做前端界面.也是晕天暗地的一边做一边学.感谢园子里的老师们,我就是一边看他们的博客一边做项目的,其中特别感谢TerrLee,他的这篇文章<一步一步学silverlight 2 入门>带我进入了silverlight的世界.

     当后来我把这个项目部署到服务器上给客户使用时,出现了一个问题.我这个项目的Xap包达到了6M,有些客户在进入主界面时,居然要等几分钟才能下载完Xap包,当然是抱怨连天了,赶紧的解决的办法,后来在silverlight帮助文档里找到了关于按需下载的说明.照着它,我分割了项目,这样也算免强过关了. 

      在项目完工后,跟朋友聚会时,听说了有一个叫Prism的架框,可以用于silverlight的开发,具体如何当时也没有听懂.直到上个月,决定把去年做的那个项目升级,并把silverlight版本定为4.0版,想到了Prism这个框架,并想把这个框架应用到升级版。才开始了学习Prism的路程,这其中也少不了看园子里其他老师的博客。

      《从十个方面理解Prism和Silverlight》

      wpf && silverlight开发框架(prism)系列教程

      就是带我了解Prism的文章。

      当然,在学习这些文章的同时,我也把Prism Library的源码看了几遍,对其框架和意路也有自已的了解。

      为了不忘记我所学的这些知识,特地把学习的笔记抄录下来,放在博客里,可能过几年我再用到这个东西时,可以拿出来温故一下 。

===================================================================================== 

     Prism 是一个复合应用程序的框架。

      上面一句话里其实包含着三个信息,我们一个个来解释。

  1.  Prism是嘛东西?

 我查过google, google对此单词的解释是“棱镜”。哦,原来它是一面镜子,那原来上面一句话就可以说成是镜子是一个复合应用程序的  框架,其实这是不对的,微软对它的解释是” Composite Application Guidance for WPF and Silverlight.”  也就  是WPFSilverlight的复合应用程序指南,最新的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到底是个什么类,它是干什么用的??

 我们打开Prism Library 源码一起来看看

      Untiybootstrapper类就定义在Prism.UntiyExtensions.Silverlight项目的UntiyBootstrapper.cs文件里,现在我们打开这个文件,看看里面的代码。

      呵呵,这个类的命名空间是Microsoft.Practice.Prism.UnityExtensions, 它继承于Bootstrapper类,又有一个基类,那里面的代码也不看了,接着找,看看Bootstrapper类是干什么的,Prism.Silverlight项目里有个Bootstrapper.cs文件,里面就是Bootstrapper类的定义。

 

    现在大家跟我一起进入到这个文件里一起看看。这个文件代码比较多,截图看不到完整的,我就给大家讲讲吧,这里面有些什么

Bootstrapper类源码
public abstract class Bootstrapper  //Bootstrapper是一个抽象类,就是说它不能实例化,你不能创建它的对象,它只能被继承
    {
         Protected  ILoggerFacade   Logger { getset; }   //保护属性,是一个专门用来写日志的类
Protected  IModuleCatalog  ModuleCatalog { getset; }  //模块目录,保护属性,是用来保存程序中所有要用到的模块信息
Protected  DependencyObject  Shell { getset; }  //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类时,一定要关注

 

         好了,接下来,我们再看看UntiyBootstrapper类里面做了些什么事

UntiyBootstrapper源码
public abstract class UnityBootstrapper : Bootstrapper  //好家伙,这也是个抽象类,不能直接拿来用的,还得继承呀
    {
        
private bool useDefaultConfiguration = true;  //是否使用默认的配置
  public IUnityContainer Container { getprotected 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类,实现其中几个方法的重载,用来加载我们的应用程序中的模块和指定初始显示的视图。继承是一定要的,那么重载哪个方法咧,就是上面用红色标注的几个方法,接下来看代码

MyBootstrapper
Class  MyBootstrapper: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管理器的对象

            }

 

 



  

posted @ 2010-11-25 11:20  jassonzhang  阅读(1404)  评论(2编辑  收藏  举报