在默認情況下,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);
}
關於模塊加載器,後續隨筆中將有專門的一節內容,敬請關注.