Prism框架中的Module概述(下)
背景
在上篇以及中篇的文章中我们介绍了Prism框架中整个Module的注册、发现、加载、初始化等过程,我们最终分析到了ModuleManager的Run方法,这个方法通过内部调用将所有加载到当前应用程序中的Module进行初始化并完成加载的过程,那么ModuleManager的Run方法又是被谁调用?在调用这个方法之前Prism又该完成哪些过程呢?本篇文章我们带着这些问题来一步步进行分析。
过程分析
1 RunModuleManager
我们发现在Prism框架中定义了一个PrismInitializationExtensions的包含静态扩展方法的类,主要完成一些系统级别的设置,我们提到的RunModuleManager这个方法就是定义在这个扩展类中的,在分析这个扩展类之前我们先来看看这个类的源码。
internal static class PrismInitializationExtensions
{
internal static void ConfigureViewModelLocator()
{
ViewModelLocationProvider.SetDefaultViewModelFactory((view, type) =>
{
return ContainerLocator.Container.Resolve(type);
});
}
internal static void RegisterRequiredTypes(this IContainerRegistry containerRegistry, IModuleCatalog moduleCatalog)
{
containerRegistry.RegisterInstance(moduleCatalog);
containerRegistry.RegisterSingleton<IDialogService, DialogService>();
containerRegistry.RegisterSingleton<IModuleInitializer, ModuleInitializer>();
containerRegistry.RegisterSingleton<IModuleManager, ModuleManager>();
containerRegistry.RegisterSingleton<RegionAdapterMappings>();
containerRegistry.RegisterSingleton<IRegionManager, RegionManager>();
containerRegistry.RegisterSingleton<IRegionNavigationContentLoader, RegionNavigationContentLoader>();
containerRegistry.RegisterSingleton<IEventAggregator, EventAggregator>();
containerRegistry.RegisterSingleton<IRegionViewRegistry, RegionViewRegistry>();
containerRegistry.RegisterSingleton<IRegionBehaviorFactory, RegionBehaviorFactory>();
containerRegistry.Register<IRegionNavigationJournalEntry, RegionNavigationJournalEntry>();
containerRegistry.Register<IRegionNavigationJournal, RegionNavigationJournal>();
containerRegistry.Register<IRegionNavigationService, RegionNavigationService>();
containerRegistry.Register<IDialogWindow, DialogWindow>(); //default dialog host
}
internal static void RegisterDefaultRegionBehaviors(this IRegionBehaviorFactory regionBehaviors)
{
regionBehaviors.AddIfMissing<BindRegionContextToDependencyObjectBehavior>(BindRegionContextToDependencyObjectBehavior.BehaviorKey);
regionBehaviors.AddIfMissing<RegionActiveAwareBehavior>(RegionActiveAwareBehavior.BehaviorKey);
regionBehaviors.AddIfMissing<SyncRegionContextWithHostBehavior>(SyncRegionContextWithHostBehavior.BehaviorKey);
regionBehaviors.AddIfMissing<RegionManagerRegistrationBehavior>(RegionManagerRegistrationBehavior.BehaviorKey);
regionBehaviors.AddIfMissing<RegionMemberLifetimeBehavior>(RegionMemberLifetimeBehavior.BehaviorKey);
regionBehaviors.AddIfMissing<ClearChildViewsRegionBehavior>(ClearChildViewsRegionBehavior.BehaviorKey);
regionBehaviors.AddIfMissing<AutoPopulateRegionBehavior>(AutoPopulateRegionBehavior.BehaviorKey);
regionBehaviors.AddIfMissing<DestructibleRegionBehavior>(DestructibleRegionBehavior.BehaviorKey);
}
internal static void RegisterDefaultRegionAdapterMappings(this RegionAdapterMappings regionAdapterMappings)
{
regionAdapterMappings.RegisterMapping<Selector, SelectorRegionAdapter>();
regionAdapterMappings.RegisterMapping<ItemsControl, ItemsControlRegionAdapter>();
regionAdapterMappings.RegisterMapping<ContentControl, ContentControlRegionAdapter>();
}
internal static void RunModuleManager(IContainerProvider containerProvider)
{
IModuleManager manager = containerProvider.Resolve<IModuleManager>();
manager.Run();
}
}
这个静态方法主要是从当前的IOC容器中获取到IModuleManager的接口实现,即我们前一节提到的ModuleManager的实现类,我们再来一步步向上分析,这个RunModuleManager方法又是在什么地方被调用的呢?
2 InitializeModules方法
这个是定义在PrismBootstrapperBase中的一个方法,在PrismBootstrapperBase这个抽象类中也有一个Initialize方法,这个方法中会调用InitializeModules方法,我们先来看看PrismBootstrapperBase中的这些方法,这里为了方便分析整个过程,我将整个类的代码都贴出从而方便进行分析。
/// <summary>
/// Base class that provides a basic bootstrapping sequence and hooks
/// that specific implementations can override
/// </summary>
/// <remarks>
/// This class must be overridden to provide application specific configuration.
/// </remarks>
public abstract class PrismBootstrapperBase
{
IContainerExtension _containerExtension;
IModuleCatalog _moduleCatalog;
/// <summary>
/// The dependency injection container used to resolve objects
/// </summary>
public IContainerProvider Container => _containerExtension;
/// <summary>
/// Gets the shell user interface
/// </summary>
/// <value>The shell user interface.</value>
protected DependencyObject Shell { get; set; }
/// <summary>
/// Runs the bootstrapper process.
/// </summary>
public void Run()
{
ConfigureViewModelLocator();
Initialize();
OnInitialized();
}
/// <summary>
/// Configures the <see cref="Prism.Mvvm.ViewModelLocator"/> used by Prism.
/// </summary>
protected virtual void ConfigureViewModelLocator()
{
PrismInitializationExtensions.ConfigureViewModelLocator();
}
/// <summary>
/// Runs the initialization sequence to configure the Prism application.
/// </summary>
protected virtual void Initialize()
{
ContainerLocator.SetContainerExtension(CreateContainerExtension);
_containerExtension = ContainerLocator.Current;
_moduleCatalog = CreateModuleCatalog();
RegisterRequiredTypes(_containerExtension);
RegisterTypes(_containerExtension);
_containerExtension.FinalizeExtension();
ConfigureModuleCatalog(_moduleCatalog);
var regionAdapterMappings = _containerExtension.Resolve<RegionAdapterMappings>();
ConfigureRegionAdapterMappings(regionAdapterMappings);
var defaultRegionBehaviors = _containerExtension.Resolve<IRegionBehaviorFactory>();
ConfigureDefaultRegionBehaviors(defaultRegionBehaviors);
RegisterFrameworkExceptionTypes();
var shell = CreateShell();
if (shell != null)
{
MvvmHelpers.AutowireViewModel(shell);
RegionManager.SetRegionManager(shell, _containerExtension.Resolve<IRegionManager>());
RegionManager.UpdateRegions();
InitializeShell(shell);
}
InitializeModules();
}
/// <summary>
/// Creates the container used by Prism.
/// </summary>
/// <returns>The container</returns>
protected abstract IContainerExtension CreateContainerExtension();
/// <summary>
/// Creates the <see cref="IModuleCatalog"/> used by Prism.
/// </summary>
/// <remarks>
/// The base implementation returns a new ModuleCatalog.
/// </remarks>
protected virtual IModuleCatalog CreateModuleCatalog()
{
return new ModuleCatalog();
}
/// <summary>
/// Registers all types that are required by Prism to function with the container.
/// </summary>
/// <param name="containerRegistry"></param>
protected virtual void RegisterRequiredTypes(IContainerRegistry containerRegistry)
{
if (_moduleCatalog == null)
throw new InvalidOperationException("IModuleCatalog");
containerRegistry.RegisterRequiredTypes(_moduleCatalog);
}
/// <summary>
/// Used to register types with the container that will be used by your application.
/// </summary>
protected abstract void RegisterTypes(IContainerRegistry containerRegistry);
/// <summary>
/// Configures the <see cref="IRegionBehaviorFactory"/>.
/// This will be the list of default behaviors that will be added to a region.
/// </summary>
protected virtual void ConfigureDefaultRegionBehaviors(IRegionBehaviorFactory regionBehaviors)
{
regionBehaviors?.RegisterDefaultRegionBehaviors();
}
/// <summary>
/// Configures the default region adapter mappings to use in the application, in order
/// to adapt UI controls defined in XAML to use a region and register it automatically.
/// May be overwritten in a derived class to add specific mappings required by the application.
/// </summary>
/// <returns>The <see cref="RegionAdapterMappings"/> instance containing all the mappings.</returns>
protected virtual void ConfigureRegionAdapterMappings(RegionAdapterMappings regionAdapterMappings)
{
regionAdapterMappings?.RegisterDefaultRegionAdapterMappings();
}
/// <summary>
/// Registers the <see cref="Type"/>s of the Exceptions that are not considered
/// root exceptions by the <see cref="ExceptionExtensions"/>.
/// </summary>
protected virtual void RegisterFrameworkExceptionTypes()
{
}
/// <summary>
/// Creates the shell or main window of the application.
/// </summary>
/// <returns>The shell of the application.</returns>
protected abstract DependencyObject CreateShell();
/// <summary>
/// Initializes the shell.
/// </summary>
protected virtual void InitializeShell(DependencyObject shell)
{
Shell = shell;
}
/// <summary>
/// Contains actions that should occur last.
/// </summary>
protected virtual void OnInitialized()
{
if (Shell is Window window)
window.Show();
}
/// <summary>
/// Configures the <see cref="IModuleCatalog"/> used by Prism.
/// </summary>
protected virtual void ConfigureModuleCatalog(IModuleCatalog moduleCatalog) { }
/// <summary>
/// Initializes the modules.
/// </summary>
protected virtual void InitializeModules()
{
PrismInitializationExtensions.RunModuleManager(Container);
}
}
这个类中我们从Run方法开始一步步进行分析从而使自己对整个Prism实现的过程有一个完整的概念。
2.1 ConfigureViewModelLocator();
这个顾名思义就是定义一种如何通过View找到ViewModel的方式,这个属于基础的配置内容,我们来看看这个最终调用的实现。
internal static void ConfigureViewModelLocator()
{
ViewModelLocationProvider.SetDefaultViewModelFactory((view, type) =>
{
return ContainerLocator.Container.Resolve(type);
});
}
我们看到这里面是为ViewModelLocationProvider设置一个委托,当传入当前View对象的时候需要到IOC容器中查找view对应type(view.GetType())的注册对象,我们获取到实际的view对象后可以通过在ViewModelLocationProvider中定义了一种默认的映射方式来找到当前view对应的ViewModel,如下所示,我们先来看看默认的映射规则:
/// <summary>
/// Default view type to view model type resolver, assumes the view model is in same assembly as the view type, but in the "ViewModels" namespace.
/// </summary>
static Func<Type, Type> _defaultViewTypeToViewModelTypeResolver =
viewType =>
{
var viewName = viewType.FullName;
viewName = viewName.Replace(".Views.", ".ViewModels.");
var viewAssemblyName = viewType.GetTypeInfo().Assembly.FullName;
var suffix = viewName.EndsWith("View") ? "Model" : "ViewModel";
var viewModelName = String.Format(CultureInfo.InvariantCulture, "{0}{1}, {2}", viewName, suffix, viewAssemblyName);
return Type.GetType(viewModelName);
};
即:如果我们当前定义在Views文件夹下面有AView对象,那么我们默认去查找ViewModels下面的AViewModel作为AView的数据上下文,当然这个只是默认的实现,我们也可以通过ViewModelLocationProvider这个静态类的SetDefaultViewTypeToViewModelTypeResolver方法来定义我们自己的映射规则,这里就不再赘述。
2.2 Initilize方法
这个是整个Prism框架中初始化要进行的所有操作,我们可以看到我们本节分析的InitilizeModules是最后一个过程,由于整个初始化过程较多,我们来大致按照顺序进行总结下:
- 设置Prism系统中使用的默认依赖注入容器。
- 创建默认的并且唯一的ModuleCatalog对象。
- 注册系统中必须的类型及其默认实现到依赖注入容器中。
- 配置创建的ModuleCatalog对象。
- 配置默认的RegionAdapterMappings。(这个会在后面的章节中重点讲述)
- 配置默认的RegionBehavior。
- 初始化全局Shell对象。
- 初始化Shell中所有的Modules
经过上面的所有过程完成整个Prism框架中的初始化过程,至此整个Prism中Module的加载过程基本上都有一个非常清晰的结构和思路了。
扩展
在上面的分析中我们看到PrismBootstrapperBase是一个抽象基类,在实际的框架中我们可以看看Prism中这个基类的集成类以及我们完整开发Prism程序的时候需要怎么来重写这些基类中的方法。
1 PrismBootstrapper
/// <summary>
/// Base bootstrapper class that uses <see cref="DryIocContainerExtension"/> as it's container.
/// </summary>
public abstract class PrismBootstrapper : PrismBootstrapperBase
{
/// <summary>
/// Create <see cref="Rules" /> to alter behavior of <see cref="IContainer" />
/// </summary>
/// <returns>An instance of <see cref="Rules" /></returns>
protected virtual Rules CreateContainerRules() => DryIocContainerExtension.DefaultRules;
/// <summary>
/// Create a new <see cref="DryIocContainerExtension"/> used by Prism.
/// </summary>
/// <returns>A new <see cref="DryIocContainerExtension"/>.</returns>
protected override IContainerExtension CreateContainerExtension()
{
return new DryIocContainerExtension(new Container(CreateContainerRules()));
}
/// <summary>
/// Registers the <see cref="Type"/>s of the Exceptions that are not considered
/// root exceptions by the <see cref="ExceptionExtensions"/>.
/// </summary>
protected override void RegisterFrameworkExceptionTypes()
{
ExceptionExtensions.RegisterFrameworkExceptionType(typeof(ContainerException));
}
}
在上面的例子中如果我们使用Prism提供的DryIoc容器的话,我们需要重写CreateContainerExtension()方法从而使用DryIOC容器,如果我们需要使用自定义的依赖注入容器的话,那么我们需要自己重写这个方法从而进行灵活配置。
2 示例Bootstrapper
class Bootstrapper : PrismBootstrapper
{
protected override DependencyObject CreateShell()
{
return Container.Resolve<MainWindow>();
}
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.RegisterSharedSamples();
}
protected override void ConfigureModuleCatalog(IModuleCatalog moduleCatalog)
{
base.ConfigureModuleCatalog(moduleCatalog);
moduleCatalog.AddModule<ModuleAModule>();
}
}
最后给一个实际的Demo的例子在我们的代码中我们需要做些什么?比如:1 将MainWindow作为Prism中唯一Shell对象。2 使用依赖注入容器注册我们自己的类型。3 根据需要去配置我们自己的ModuleCatalog对象等等。