[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,今天先写这么多,非常感谢大家,周末愉快