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