• Prism中Region(上)


    概述

      在介绍Region之前我们首先有必要了解什么是Region(区域)简单来说,Region可以理解为View的动态占位符,在View上进行视图占位,就像在电影院占座一样,方便后续注入UI元素,比如我们可以将我们的程序划分为MenuRegion,Top Region、和Main Region,我们之前并不知道这几块区域到底会放置什么东西?因此我们先进行布局站位,比如使用ContentControl设置当前的区域名称,如下面的代码所示:

    <ContentControl prism:RegionManager.RegionName="MenuRegion" />
    

      在后面我们就可以根据需要为这个名称为MenuRegion的区域注入真正地View对象,这样做的目的是为了真正地实现界面的解耦,这个是整个Region设计的核心思想。

    源码分析

    1 IRegion接口

      按照我们之前读源码系列的思路,我们首先还是从最基本的接口进行一个个分析。

        /// <summary>
        /// Defines a model that can be used to compose views.
        /// </summary>
        public interface IRegion : INavigateAsync, INotifyPropertyChanged
        {
            /// <summary>
            /// Gets a readonly view of the collection of views in the region.
            /// </summary>
            /// <value>An <see cref="IViewsCollection"/> of all the added views.</value>
            IViewsCollection Views { get; }
    
            /// <summary>
            /// Gets a readonly view of the collection of all the active views in the region.
            /// </summary>
            /// <value>An <see cref="IViewsCollection"/> of all the active views.</value>
            IViewsCollection ActiveViews { get; }
    
            /// <summary>
            /// Gets or sets a context for the region. This value can be used by the user to share context with the views.
            /// </summary>
            /// <value>The context value to be shared.</value>
            object Context { get; set; }
    
            /// <summary>
            /// Gets the name of the region that uniquely identifies the region within a <see cref="IRegionManager"/>.
            /// </summary>
            /// <value>The name of the region.</value>
            string Name { get; set; }
    
            /// <summary>
            /// Gets or sets the comparison used to sort the views.
            /// </summary>
            /// <value>The comparison to use.</value>
            Comparison<object> SortComparison { get; set; }
    
            ///<overloads>Adds a new view to the region.</overloads>
            /// <summary>
            /// Adds a new view to the region.
            /// </summary>
            /// <param name="view">The view to add.</param>
            /// <returns>The <see cref="IRegionManager"/> that is set on the view if it is a <see cref="DependencyObject"/>. It will be the current region manager when using this overload.</returns>
            IRegionManager Add(object view);
    
            /// <summary>
            /// Adds a new view to the region.
            /// </summary>
            /// <param name="view">The view to add.</param>
            /// <param name="viewName">The name of the view. This can be used to retrieve it later by calling <see cref="GetView"/>.</param>
            /// <returns>The <see cref="IRegionManager"/> that is set on the view if it is a <see cref="DependencyObject"/>. It will be the current region manager when using this overload.</returns>
            IRegionManager Add(object view, string viewName);
    
            /// <summary>
            /// Adds a new view to the region.
            /// </summary>
            /// <param name="view">The view to add.</param>
            /// <param name="viewName">The name of the view. This can be used to retrieve it later by calling <see cref="GetView"/>.</param>
            /// <param name="createRegionManagerScope">When <see langword="true"/>, the added view will receive a new instance of <see cref="IRegionManager"/>, otherwise it will use the current region manager for this region.</param>
            /// <returns>The <see cref="IRegionManager"/> that is set on the view if it is a <see cref="DependencyObject"/>.</returns>
            IRegionManager Add(object view, string viewName, bool createRegionManagerScope);
    
            /// <summary>
            /// Removes the specified view from the region.
            /// </summary>
            /// <param name="view">The view to remove.</param>
            void Remove(object view);
    
            /// <summary>
            /// Removes all views from the region.
            /// </summary>
            void RemoveAll();
    
            /// <summary>
            /// Marks the specified view as active. 
            /// </summary>
            /// <param name="view">The view to activate.</param>
            void Activate(object view);
    
            /// <summary>
            /// Marks the specified view as inactive. 
            /// </summary>
            /// <param name="view">The view to deactivate.</param>
            void Deactivate(object view);
    
            /// <summary>
            /// Returns the view instance that was added to the region using a specific name.
            /// </summary>
            /// <param name="viewName">The name used when adding the view to the region.</param>
            /// <returns>Returns the named view or <see langword="null"/> if the view with <paramref name="viewName"/> does not exist in the current region.</returns>
            object GetView(string viewName);
    
            /// <summary>
            /// Gets or sets the <see cref="IRegionManager"/> that will be passed to the views when adding them to the region, unless the view is added by specifying createRegionManagerScope as <see langword="true" />.
            /// </summary>
            /// <value>The <see cref="IRegionManager"/> where this <see cref="IRegion"/> is registered.</value>
            /// <remarks>This is usually used by implementations of <see cref="IRegionManager"/> and should not be
            /// used by the developer explicitly.</remarks>
            IRegionManager RegionManager { get; set; }
    
            /// <summary>
            /// Gets the collection of <see cref="IRegionBehavior"/>s that can extend the behavior of regions. 
            /// </summary>
            IRegionBehaviorCollection Behaviors { get; }
    
            /// <summary>
            /// Gets or sets the navigation service.
            /// </summary>
            /// <value>The navigation service.</value>
            IRegionNavigationService NavigationService { get; set; }
        }
    

     在我们了解IRegion内部关联的对象之前,我们来看看IRegion接口继承了哪些接口,首先就是INavigateAsync接口,初一看这个接口不太清楚什么意思,但是我们可以通过类比浏览器的导航功能做一个对比,然后我们结合IRegion接口中定义的ViewsActiveViews属性我们便有一个清晰的认识就是一个IRegion中会有多个View我们可以通过传入不同的URL从而控制当前的Region中到底显示的是哪一个界面?这个场景是不是非常常见,想想一下我们的微信、QQ等软件,左侧是联系人,右边是具体信息界面,当我们点击左侧不同联系人时右侧的Region区域会显示不同的详细信息,而这些不同信息都是在一个Region中进行展示的,有了这个解释是不是非常形象。

    1.1 INavigateAsync接口

    /// <summary>
        /// Provides methods to perform navigation.
        /// </summary>
        /// <remarks>
        /// Convenience overloads for the methods in this interface can be found as extension methods on the 
        /// <see cref="NavigationAsyncExtensions"/> class.
        /// </remarks>
        public interface INavigateAsync
        {
            /// <summary>
            /// Initiates navigation to the target specified by the <see cref="Uri"/>.
            /// </summary>
            /// <param name="target">The navigation target</param>
            /// <param name="navigationCallback">The callback executed when the navigation request is completed.</param>
            /// <remarks>
            /// Convenience overloads for this method can be found as extension methods on the 
            /// <see cref="NavigationAsyncExtensions"/> class.
            /// </remarks>
            void RequestNavigate(Uri target, Action<NavigationResult> navigationCallback);
    
            /// <summary>
            /// Initiates navigation to the target specified by the <see cref="Uri"/>.
            /// </summary>
            /// <param name="target">The navigation target</param>
            /// <param name="navigationCallback">The callback executed when the navigation request is completed.</param>
            /// <param name="navigationParameters">The navigation parameters specific to the navigation request.</param>
            /// <remarks>
            /// Convenience overloads for this method can be found as extension methods on the 
            /// <see cref="NavigationAsyncExtensions"/> class.
            /// </remarks>
            void RequestNavigate(Uri target, Action<NavigationResult> navigationCallback, NavigationParameters navigationParameters);
        }
    

     IRegion实现的第二个接口是INotifyPropertyChanged接口,这个就不用做过多的解释主要就是属性变更通知UI的,这个就在这里不做过多解释。
     在了解完了IRegion继承的外部接口之前我们先来了解其内如关联的相关接口

    1.2 IViewsCollection接口

    /// <summary>
        /// Defines a view of a collection.
        /// </summary>
        public interface IViewsCollection : IEnumerable<object>, INotifyCollectionChanged
        {
            /// <summary>
            /// Determines whether the collection contains a specific value.
            /// </summary>
            /// <param name="value">The object to locate in the collection.</param>
            /// <returns><see langword="true" /> if <paramref name="value"/> is found in the collection; otherwise, <see langword="false" />.</returns>
            bool Contains(object value);
        }
    

     这个是用在IRegion接口中的Views和ActiveViews属性的,我们刚才说过一个IRegion中包含多个View对象并且通过实现INavigateAsync接口来实现不同的视图对象的切换,这里通过接口定义我们大概就了解其内部的实现,围绕着这个 IViewsCollection对象我们发现在IRegion接口中关于View的Add、Remove、Active、DeActive都是和这个直接相关的,通过接口定义我们就能够对整个Region包含的功能有一个清晰的认知。

    1.3 IRegionManager接口

     这个我们将会在后面的文章中就这个做单独的分析,这里我们需要注意的是一个IRegion对应一个IRegionManager,一个IRegionManager用来管理当前IRegion中的各种View对象,本节我们不重点去介绍这个部分。

    1.4 IRegionBehavior接口

     这个我们也将会在后面的文章中就这个做单独的分析,这个接口主要是为当前的IRegion添加各种各种的Behavior,通过实现这个接口我们能为当前Region添加各种各样的行为而且代码结构上会更加统一和标准,这个也是Prism框架设计的优秀地方。

    /// <summary>
        /// Interface for allowing extensible behavior on regions.
        /// </summary>
        public interface IRegionBehavior
        {
            /// <summary>
            /// The region that this behavior is extending.
            /// </summary>
            IRegion Region { get; set; }
    
            /// <summary>
            /// Attaches the behavior to the specified region.
            /// </summary>
            void Attach();
    
        }
    

    1.5 IRegionNavigationServicer接口

     在上面的分析中我们发现IRegionNavigationService这个接口主要是实现IRegion中Views实现导航功能,这个IRegionNavigationService这个接口也实现了INavigateAsync这个接口,所以我们可以猜测IRegion中的导航功能最终是通过IRegion内部关联的
    IRegionNavigationService接口实现的,而且IRegionNavigationService这个接口内部包含导航时的各种技术细节,并且通过时间向外通知当前导航的状态,这个部分后面我们也将分章节去介绍这个部分的具体实现。

    /// <summary>
        /// Provides navigation for regions.
        /// </summary>
        public interface IRegionNavigationService : INavigateAsync
        {
            /// <summary>
            /// Gets or sets the region owning this service.
            /// </summary>
            /// <value>A Region.</value>
            IRegion Region { get; set; }
    
            /// <summary>
            /// Gets the journal.
            /// </summary>
            /// <value>The journal.</value>
            IRegionNavigationJournal Journal { get; }
    
            /// <summary>
            /// Raised when the region is about to be navigated to content.
            /// </summary>
            event EventHandler<RegionNavigationEventArgs> Navigating;
    
            /// <summary>
            /// Raised when the region is navigated to content.
            /// </summary>
            event EventHandler<RegionNavigationEventArgs> Navigated;
    
            /// <summary>
            /// Raised when a navigation request fails.
            /// </summary>
            event EventHandler<RegionNavigationFailedEventArgs> NavigationFailed;
        }
    

     至此通过对IRegion接口中的分析,我们对于整个Region的功能有一个大概的了解,并对每一个关联以及继承的接口都有清晰的认知,我们先抓住整个脉络从而对整体有一个清晰的认识,后面再深入细节,这样我们理解整个框架就容易多了。

    2 Region实现

     这个部分看着代码很多,其实有了上面的分析你大概对这个部分的具体实现是怎么样的?而且我们最重要的是学习这些框架的封装思想,这个才是最重要的。

    /// <summary>
        /// Implementation of <see cref="IRegion"/> that allows multiple active views.
        /// </summary>
        public class Region : IRegion
        {
            private ObservableCollection<ItemMetadata> _itemMetadataCollection;
            private string _name;
            private ViewsCollection _views;
            private ViewsCollection _activeViews;
            private object _context;
            private IRegionManager _regionManager;
            private IRegionNavigationService _regionNavigationService;
    
            private Comparison<object> _sort;
    
            /// <summary>
            /// Initializes a new instance of <see cref="Region"/>.
            /// </summary>
            public Region()
            {
                Behaviors = new RegionBehaviorCollection(this);
    
                _sort = DefaultSortComparison;
            }
    
            /// <summary>
            /// Occurs when a property value changes.
            /// </summary>
            public event PropertyChangedEventHandler PropertyChanged;
    
            /// <summary>
            /// Gets the collection of <see cref="IRegionBehavior"/>s that can extend the behavior of regions.
            /// </summary>
            public IRegionBehaviorCollection Behaviors { get; }
    
            /// <summary>
            /// Gets or sets a context for the region. This value can be used by the user to share context with the views.
            /// </summary>
            /// <value>The context value to be shared.</value>
            public object Context
            {
                get => _context;
    
                set
                {
                    if (_context != value)
                    {
                        _context = value;
                        OnPropertyChanged(nameof(Context));
                    }
                }
            }
    
            /// <summary>
            /// Gets the name of the region that uniquely identifies the region within a <see cref="IRegionManager"/>.
            /// </summary>
            /// <value>The name of the region.</value>
            public string Name
            {
                get => _name;
    
                set
                {
                    if (_name != null && _name != value)
                    {
                        throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, Resources.CannotChangeRegionNameException, _name));
                    }
    
                    if (string.IsNullOrEmpty(value))
                    {
                        throw new ArgumentException(Resources.RegionNameCannotBeEmptyException);
                    }
    
                    _name = value;
                    OnPropertyChanged(nameof(Name));
                }
            }
    
            /// <summary>
            /// Gets a readonly view of the collection of views in the region.
            /// </summary>
            /// <value>An <see cref="IViewsCollection"/> of all the added views.</value>
            public virtual IViewsCollection Views
            {
                get
                {
                    if (_views == null)
                    {
                        _views = new ViewsCollection(ItemMetadataCollection, x => true)
                        {
                            SortComparison = _sort
                        };
                    }
    
                    return _views;
                }
            }
    
            /// <summary>
            /// Gets a readonly view of the collection of all the active views in the region.
            /// </summary>
            /// <value>An <see cref="IViewsCollection"/> of all the active views.</value>
            public virtual IViewsCollection ActiveViews
            {
                get
                {
                    if (_views == null)
                    {
                        _views = new ViewsCollection(ItemMetadataCollection, x => true)
                        {
                            SortComparison = _sort
                        };
                    }
    
                    if (_activeViews == null)
                    {
                        _activeViews = new ViewsCollection(ItemMetadataCollection, x => x.IsActive)
                        {
                            SortComparison = _sort
                        };
                    }
    
                    return _activeViews;
                }
            }
    
            /// <summary>
            /// Gets or sets the comparison used to sort the views.
            /// </summary>
            /// <value>The comparison to use.</value>
            public Comparison<object> SortComparison
            {
                get => _sort;
                set
                {
                    _sort = value;
    
                    if (_activeViews != null)
                    {
                        _activeViews.SortComparison = _sort;
                    }
    
                    if (_views != null)
                    {
                        _views.SortComparison = _sort;
                    }
                }
            }
    
            /// <summary>
            /// Gets or sets the <see cref="IRegionManager"/> that will be passed to the views when adding them to the region, unless the view is added by specifying createRegionManagerScope as <see langword="true" />.
            /// </summary>
            /// <value>The <see cref="IRegionManager"/> where this <see cref="IRegion"/> is registered.</value>
            /// <remarks>This is usually used by implementations of <see cref="IRegionManager"/> and should not be
            /// used by the developer explicitly.</remarks>
            public IRegionManager RegionManager
            {
                get => _regionManager;
    
                set
                {
                    if (_regionManager != value)
                    {
                        _regionManager = value;
                        OnPropertyChanged(nameof(RegionManager));
                    }
                }
            }
    
            /// <summary>
            /// Gets the navigation service.
            /// </summary>
            /// <value>The navigation service.</value>
            public IRegionNavigationService NavigationService
            {
                get
                {
                    if (_regionNavigationService == null)
                    {
                        _regionNavigationService = ContainerLocator.Container.Resolve<IRegionNavigationService>();
                        _regionNavigationService.Region = this;
                    }
    
                    return _regionNavigationService;
                }
    
                set => _regionNavigationService = value;
            }
    
            /// <summary>
            /// Gets the collection with all the views along with their metadata.
            /// </summary>
            /// <value>An <see cref="ObservableCollection{T}"/> of <see cref="ItemMetadata"/> with all the added views.</value>
            protected virtual ObservableCollection<ItemMetadata> ItemMetadataCollection
            {
                get
                {
                    if (_itemMetadataCollection == null)
                    {
                        _itemMetadataCollection = new ObservableCollection<ItemMetadata>();
                    }
    
                    return _itemMetadataCollection;
                }
            }
    
            /// <overloads>Adds a new view to the region.</overloads>
            /// <summary>
            /// Adds a new view to the region.
            /// </summary>
            /// <param name="view">The view to add.</param>
            /// <returns>The <see cref="IRegionManager"/> that is set on the view if it is a <see cref="DependencyObject"/>. It will be the current region manager when using this overload.</returns>
            public IRegionManager Add(object view)
            {
                return this.Add(view, null, false);
            }
    
            /// <summary>
            /// Adds a new view to the region.
            /// </summary>
            /// <param name="view">The view to add.</param>
            /// <param name="viewName">The name of the view. This can be used to retrieve it later by calling <see cref="IRegion.GetView"/>.</param>
            /// <returns>The <see cref="IRegionManager"/> that is set on the view if it is a <see cref="DependencyObject"/>. It will be the current region manager when using this overload.</returns>
            public IRegionManager Add(object view, string viewName)
            {
                if (string.IsNullOrEmpty(viewName))
                {
                    throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, Resources.StringCannotBeNullOrEmpty, "viewName"));
                }
    
                return this.Add(view, viewName, false);
            }
    
            /// <summary>
            /// Adds a new view to the region.
            /// </summary>
            /// <param name="view">The view to add.</param>
            /// <param name="viewName">The name of the view. This can be used to retrieve it later by calling <see cref="IRegion.GetView"/>.</param>
            /// <param name="createRegionManagerScope">When <see langword="true"/>, the added view will receive a new instance of <see cref="IRegionManager"/>, otherwise it will use the current region manager for this region.</param>
            /// <returns>The <see cref="IRegionManager"/> that is set on the view if it is a <see cref="DependencyObject"/>.</returns>
            public virtual IRegionManager Add(object view, string viewName, bool createRegionManagerScope)
            {
                IRegionManager manager = createRegionManagerScope ? this.RegionManager.CreateRegionManager() : this.RegionManager;
                this.InnerAdd(view, viewName, manager);
                return manager;
            }
    
            /// <summary>
            /// Removes the specified view from the region.
            /// </summary>
            /// <param name="view">The view to remove.</param>
            public virtual void Remove(object view)
            {
                ItemMetadata itemMetadata = this.GetItemMetadataOrThrow(view);
    
                ItemMetadataCollection.Remove(itemMetadata);
    
                if (view is DependencyObject dependencyObject && Regions.RegionManager.GetRegionManager(dependencyObject) == this.RegionManager)
                {
                    dependencyObject.ClearValue(Regions.RegionManager.RegionManagerProperty);
                }
            }
    
            /// <summary>
            /// Removes all views from the region.
            /// </summary>
            public void RemoveAll()
            {
                foreach (var view in Views)
                {
                    Remove(view);
                }
            }
    
            /// <summary>
            /// Marks the specified view as active.
            /// </summary>
            /// <param name="view">The view to activate.</param>
            public virtual void Activate(object view)
            {
                ItemMetadata itemMetadata = this.GetItemMetadataOrThrow(view);
    
                if (!itemMetadata.IsActive)
                {
                    itemMetadata.IsActive = true;
                }
            }
    
            /// <summary>
            /// Marks the specified view as inactive.
            /// </summary>
            /// <param name="view">The view to deactivate.</param>
            public virtual void Deactivate(object view)
            {
                ItemMetadata itemMetadata = this.GetItemMetadataOrThrow(view);
    
                if (itemMetadata.IsActive)
                {
                    itemMetadata.IsActive = false;
                }
            }
    
            /// <summary>
            /// Returns the view instance that was added to the region using a specific name.
            /// </summary>
            /// <param name="viewName">The name used when adding the view to the region.</param>
            /// <returns>Returns the named view or <see langword="null"/> if the view with <paramref name="viewName"/> does not exist in the current region.</returns>
            public virtual object GetView(string viewName)
            {
                if (string.IsNullOrEmpty(viewName))
                {
                    throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, Resources.StringCannotBeNullOrEmpty, "viewName"));
                }
    
                ItemMetadata metadata = this.ItemMetadataCollection.FirstOrDefault(x => x.Name == viewName);
    
                if (metadata != null)
                {
                    return metadata.Item;
                }
    
                return null;
            }
    
            /// <summary>
            /// Initiates navigation to the specified target.
            /// </summary>
            /// <param name="target">The target.</param>
            /// <param name="navigationCallback">A callback to execute when the navigation request is completed.</param>
            public void RequestNavigate(Uri target, Action<NavigationResult> navigationCallback)
            {
                this.RequestNavigate(target, navigationCallback, null);
            }
    
            /// <summary>
            /// Initiates navigation to the specified target.
            /// </summary>
            /// <param name="target">The target.</param>
            /// <param name="navigationCallback">A callback to execute when the navigation request is completed.</param>
            /// <param name="navigationParameters">The navigation parameters specific to the navigation request.</param>
            public void RequestNavigate(Uri target, Action<NavigationResult> navigationCallback, NavigationParameters navigationParameters)
            {
                this.NavigationService.RequestNavigate(target, navigationCallback, navigationParameters);
            }
    
            private void InnerAdd(object view, string viewName, IRegionManager scopedRegionManager)
            {
                if (this.ItemMetadataCollection.FirstOrDefault(x => x.Item == view) != null)
                {
                    throw new InvalidOperationException(Resources.RegionViewExistsException);
                }
    
                ItemMetadata itemMetadata = new ItemMetadata(view);
                if (!string.IsNullOrEmpty(viewName))
                {
                    if (this.ItemMetadataCollection.FirstOrDefault(x => x.Name == viewName) != null)
                    {
                        throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, Resources.RegionViewNameExistsException, viewName));
                    }
                    itemMetadata.Name = viewName;
                }
    
    
                if (view is DependencyObject dependencyObject)
                {
                    Regions.RegionManager.SetRegionManager(dependencyObject, scopedRegionManager);
                }
    
                this.ItemMetadataCollection.Add(itemMetadata);
            }
    
            private ItemMetadata GetItemMetadataOrThrow(object view)
            {
                if (view == null)
                    throw new ArgumentNullException(nameof(view));
    
                ItemMetadata itemMetadata = this.ItemMetadataCollection.FirstOrDefault(x => x.Item == view);
    
                if (itemMetadata == null)
                    throw new ArgumentException(Resources.ViewNotInRegionException, nameof(view));
    
                return itemMetadata;
            }
    
            private void OnPropertyChanged(string propertyName)
            {
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
            }
    
            /// <summary>
            /// The default sort algorithm.
            /// </summary>
            /// <param name="x">The first view to compare.</param>
            /// <param name="y">The second view to compare.</param>
            /// <returns></returns>
            [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "y")]
            [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "x")]
            public static int DefaultSortComparison(object x, object y)
            {
                if (x == null)
                {
                    if (y == null)
                    {
                        return 0;
                    }
                    else
                    {
                        return -1;
                    }
                }
                else
                {
                    if (y == null)
                    {
                        return 1;
                    }
                    else
                    {
                        Type xType = x.GetType();
                        Type yType = y.GetType();
    
                        ViewSortHintAttribute xAttribute = xType.GetCustomAttributes(typeof(ViewSortHintAttribute), true).FirstOrDefault() as ViewSortHintAttribute;
                        ViewSortHintAttribute yAttribute = yType.GetCustomAttributes(typeof(ViewSortHintAttribute), true).FirstOrDefault() as ViewSortHintAttribute;
    
                        return ViewSortHintAttributeComparison(xAttribute, yAttribute);
                    }
                }
            }
    
            private static int ViewSortHintAttributeComparison(ViewSortHintAttribute x, ViewSortHintAttribute y)
            {
                if (x == null)
                {
                    if (y == null)
                    {
                        return 0;
                    }
                    else
                    {
                        return -1;
                    }
                }
                else
                {
                    if (y == null)
                    {
                        return 1;
                    }
                    else
                    {
                        return string.Compare(x.Hint, y.Hint, StringComparison.Ordinal);
                    }
                }
            }
        }
    

    2.1 Region中Views的管理

     这个部分我就不做每一部分代码详细分析了,重点缕清楚其背后设计的思想。首先无论是Views还是ActiveViews代码中通过一个ObservableCollection类型的ItemMetadataCollection进行封装,并且其内部通过IsActive属性来标识哪些是Active的对象,另外在ItemMetadata的内部如果IsActive属性发生了变化,那么会有一个MetadataChanged事件来触发通知,另外在外部调用Add的时候内部除了往ItemMetadataCollection集合中添加View的包装对象ItemMetadata以外,还有一个重要的事情就是为当前的View设置默认的RegionManager中定义的附加属性RegionManager,这个理解有点绕,我们发现一个IRegion对应唯一的IRegionManager,而一个IRegion中会存在多个View,这些View在人状态下共享IRegion关联的唯一IRegionManager,这个就是其想表达的核心思想。

    2.2 Region中的Context

     在当前的Region中暂时没看清楚这个Context的作用,不过其注释其实说的很清楚就是为这些Views提供一个共享的数据上下文。

    /// <summary>
            /// Gets or sets a context for the region. This value can be used by the user to share context with the views.
            /// </summary>
            /// <value>The context value to be shared.</value>
            public object Context
            {
                get => _context;
    
                set
                {
                    if (_context != value)
                    {
                        _context = value;
                        OnPropertyChanged(nameof(Context));
                    }
                }
            }
    

    2.3 Region中的Navigation

     IRegion中实现了INavigateAsync接口实现了View的导航功能,在Region的内部是通过一个前面分析过的IRegionNavigationService类型的NavigationService去实现的,其内部的具体原理后面分析具体代码的时候再进行讲述。

    2.4 Region中的SortComparison

     我们在看Region这个部分的代码时我们发现,很多的篇幅是介绍同一个Region内部多个Views的排序规则的,这个主要是通过定义一个规则让这些Views有一个先后顺序,这个决定后面View加载的一些细节,甚至我们可以看到通过在View上面添加自定义属性ViewSortHint我们能够人为的对其先后顺序进行排序,这个需要注意。

    public static int DefaultSortComparison(object x, object y)
            {
                if (x == null)
                {
                    if (y == null)
                    {
                        return 0;
                    }
                    else
                    {
                        return -1;
                    }
                }
                else
                {
                    if (y == null)
                    {
                        return 1;
                    }
                    else
                    {
                        Type xType = x.GetType();
                        Type yType = y.GetType();
    
                        ViewSortHintAttribute xAttribute = xType.GetCustomAttributes(typeof(ViewSortHintAttribute), true).FirstOrDefault() as ViewSortHintAttribute;
                        ViewSortHintAttribute yAttribute = yType.GetCustomAttributes(typeof(ViewSortHintAttribute), true).FirstOrDefault() as ViewSortHintAttribute;
    
                        return ViewSortHintAttributeComparison(xAttribute, yAttribute);
                    }
                }
            }
    

    总结

     有了上面从IRegion接口的定义到IRegion接口的实现我们对整个Prism框架中的Region有一个清晰的认知,在后面的章节中我们将会对其中的技术细节,比如IRegionManager、IRegionNavigationService的具体实现分章节进行一一讲述,力求将整个Prism框架中由外到内,由总体到局部一一分析从而使自己有一个更加清晰的认知。

  • 相关阅读:
    HTTP 方法:GET 对比 POST
    js中return的用法
    Javascript:谈谈JS的全局变量跟局部变量
    ajax请求数据之后在已经有的数据前面打对勾的方法
    JS中的call()和apply()方法区别
    聚簇索引与非聚簇索引的区别
    Android开发(27)--TextView单击链接弹出Activity
    Android 4.2启动代码分析(一)
    Android重启应用程序代码
    java.lang.ClassNotFoundException: org.springframework.web.context.ContextLoaderListener
  • 原文地址:https://www.cnblogs.com/seekdream/p/16063456.html
Copyright © 2020-2023  润新知