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是最后一个过程,由于整个初始化过程较多,我们来大致按照顺序进行总结下:

  1. 设置Prism系统中使用的默认依赖注入容器。
  2. 创建默认的并且唯一的ModuleCatalog对象。
  3. 注册系统中必须的类型及其默认实现到依赖注入容器中。
  4. 配置创建的ModuleCatalog对象。
  5. 配置默认的RegionAdapterMappings。(这个会在后面的章节中重点讲述)
  6. 配置默认的RegionBehavior。
  7. 初始化全局Shell对象。
  8. 初始化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对象等等。

posted @ 2022-03-27 16:36  Hello——寻梦者!  阅读(694)  评论(0编辑  收藏  举报