[Prism]Composite Application Guidance for WPF(4)——Bootstrapper

                              [Prism]Composite Application Guidance for WPF(4)——Bootstrapper

                                           周银辉

 

在默认情况下,WPF程序的启动方式APP的XAML中指定StartUri,然后IDE会自动帮我们生成一个Main方法,然后将StartUri中指定的窗口New一个出来,并作为应用程序的主窗口,但我们在Composite Application Guidance for WPF(3)——创建第一个Composite WPF Application (如果你不了解Prism的启动方式,那么建议你阅读) 中改变了这种方式:

        public App()
        {
            var boot = new Bootstrapper();
            boot.Run();
        }

 

而Bootstrapper类似于这样的类型:

 

    class Bootstrapper : UnityBootstrapper
    {
        protected override DependencyObject CreateShell()
        {
            var shell = new Shell();

            shell.Show();

            return shell;
        }

        protected override IModuleEnumerator GetModuleEnumerator()
        {
            var configStory = new ConfigurationStore();
            var enumerator = new ConfigurationModuleEnumerator(configStory);

            return enumerator;
        }
    }

其中这里的Shell实质上是我们应用程序的主窗口,可以看出来,其是在CreateShell()方法中将主窗口显示出来的,为什么要这样呢,那么我们就来看看Prism中的Bootstrapper

 

1,Bootstrapper是什么?

在Prism中,Bootstrapper是应用程序的启动器,其职责在于可以让你对应用程序的启动过程有着更好的控制.比如加载哪些模块,如何加载模块,注册哪些服务等等.在默认情况下Prism的UnityBootstrapper作为默认的加载器已经为你完成了很多工作,比如依赖注入容器的建立,Region映射,初始化模块的等,我们只要指定Shell和模块枚举器(IModuleEnumerator)便可以了,但很好的一点是,在Bootstrapper中很多方法都是虚方法(也包括抽象方法),我们可以通过重写这些方法来更改启动内容和启动逻辑.

 

2,Bootstrapper完成了哪些工作?

 

 

 上图是Prism的帮助文档给出了,从中我们可以看到其主要完成了4方面的工作:配置依赖注入容器,配置Region映射,创建Shell,初始化模块, 而打开Prism的源代码就更清晰了(在下面的Prism源代码中,我删除了一些可能会干扰视线的代码):

        public void Run(bool useDefaultConfiguration)
        {
            _useDefaultConfiguration = useDefaultConfiguration;
            //创建日志记录器
            ILoggerFacade logger = LoggerFacade;
        
            //创建默认容器
            Container = CreateContainer();
          
            //配置容器
            ConfigureContainer();

            //配置Region适配器映射
            ConfigureRegionAdapterMappings();

            //创建Shell
            DependencyObject shell = CreateShell();

            if (shell != null)
            {
                RegionManager.SetRegionManager(shell, Container.Resolve<IRegionManager>());
            }

            //初始化模块
            InitializeModules();

        }

 

2.1 创建日志记录器

Prism中自带了一个Logger,其原理很简单,利用的是Trace:

    public class TraceLogger : ILoggerFacade
    {
        /// <summary>
        /// 按照指定的 category 与 priority 记录一条新的日志
        /// </summary>
        /// <param name="message">日志消息</param>
        /// <param name="category">记录类型</param>
        /// <param name="priority">该条记录的优先级</param>
        public void Log(string message, Category category, Priority priority)
        {
            if (category == Category.Exception)
            {
                Trace.TraceError(message);
            }
            else
            {
                Trace.TraceInformation(message);
            }
        }
    }

在Prism中到处都能看到它的身影

 

2.2, 创建默认容器

依赖注入容器在Prism中(也包括其他Compsite Application框架,如CAB)扮演着最重要的角色,因为我们需要它来进行依赖注入(关于依赖注入,可以参考这里[转] 依赖注入&控制反转 ioC 容器和Dependency Injection 模式(中文版)) 

Prism在这个过程中主要完成的是一些基本组建和服务的注册

 

        protected virtual void ConfigureContainer()
        {
            //向容器注册日志实例
            Container.RegisterInstance(LoggerFacade);
            //容器注册自己
            Container.RegisterInstance(Container);
            //向容器添加一个扩展,其用于检查指定的类型是否已经在容器中注册
            Container.AddNewExtension<UnityBootstrapperExtension>();
            //模块枚举器
            IModuleEnumerator moduleEnumerator = GetModuleEnumerator();
            if (moduleEnumerator != null)
            {
                //向容器注册模块枚举器
                Container.RegisterInstance(moduleEnumerator);
            }

            //如果使用默认配置,则向容器注册CAL基础服务
            if (_useDefaultConfiguration)
            {
                RegisterTypeIfMissing(typeof (IContainerFacade), typeof (UnityContainerAdapter), true);
                RegisterTypeIfMissing(typeof (IEventAggregator), typeof (EventAggregator), true);
                RegisterTypeIfMissing(typeof (RegionAdapterMappings), typeof (RegionAdapterMappings), true);
                RegisterTypeIfMissing(typeof (IRegionManager), typeof (RegionManager), true);
                RegisterTypeIfMissing(typeof (IModuleLoader), typeof (ModuleLoader), true);
            }
        }

 

2.3 配置Region适配器映射
我们知道Region作为一个占位符,可以让其作为View的容器,而哪些控件类型具有此功能呢,至少我们知道ContentControl,ItemsControl等可以,事实上,只要有着对应Region适配器的都可以,而"配置Region适配器映射"便是将可以作为容器的控件类型与对应的适配器关联起来.

默认情况下,Prism为我们提供了3中适配器,也就对应着3种容器控件类型:Selector,ItemsControl,ContentControl,这3种控件类型以及其子类型都可以作为Region容器

        protected virtual RegionAdapterMappings ConfigureRegionAdapterMappings()
        {
            var regionAdapterMappings = Container.TryResolve<RegionAdapterMappings>();
            if (regionAdapterMappings != null)
            {
                //CAL默认提供的三种Region适配器
                regionAdapterMappings.RegisterMapping(typeof (Selector), new SelectorRegionAdapter());
                regionAdapterMappings.RegisterMapping(typeof (ItemsControl), new ItemsControlRegionAdapter());
                regionAdapterMappings.RegisterMapping(typeof (ContentControl), new ContentControlRegionAdapter());
            }

            return regionAdapterMappings;
        }

如果我们想要创建一种新的Region容器类型,那么我们需要做的是为该类型打造一个对应的XXXRegionAdapter(继承于RegionAdapterBase<T>类),然后重写ConfigureRegionAdapterMappings()方法并将容器类型和适配器注册起来就可以了.

 

2.4 创建Shell

 在默认的Bootstrapper UnityBootstrapper中,CreateShell是一个抽象方法,所以你必须自己定义Shell的创建过程,一般也来得非常简单,只要初始化你的Shell并返回就可以了:

        protected override DependencyObject CreateShell()
        {
            var shell = new Shell();

            shell.Show();

            return shell;
        }

 

2.5 初始化模块

对于模块的加载,Prism提供了几种方式,一是静态引用加载(和普通的程序集引用一样),二是动态加载(又分为扫描指定文件夹和读取配置文件两种);对应不同的加载方式就有着不同的模块加载器,在"初始化模块"这一步骤中最基本的便是取得模块加载器,然后在取得那些需要在引用程序启动时加载的模块,并将他们加载进来:

        protected virtual void InitializeModules()
        {
            var moduleEnumerator = Container.TryResolve<IModuleEnumerator>();
           

            var moduleLoader = Container.TryResolve<IModuleLoader>();
         
            ModuleInfo[] moduleInfo = moduleEnumerator.GetStartupLoadedModules();

 

            moduleLoader.Initialize(moduleInfo);
        }

 

关于模块加载器,后续随笔中将有专门的一节内容,敬请关注.

 

OK,今天先写这么多,非常感谢大家,周末愉快

 

posted @ 2008-08-23 14:47  周银辉  阅读(6701)  评论(7编辑  收藏  举报