• 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对象等等。

  • 相关阅读:
    hbase
    2013年实习
    Distinct Subsequences
    LumiSoft
    Implicit super constructor xx() is undefined for default constructor. Must define an explicit constructor
    XmlDocument.LoadXml和Load的区别
    应输入 #endregion 指令报错的排查技巧
    c#删除list中的元素
    Dragon Balls(hdu3635带权并查集)
    Java实现 蓝桥杯 算法训练 寻找数组中最大值
  • 原文地址:https://www.cnblogs.com/seekdream/p/16063311.html
Copyright © 2020-2023  润新知