1. 程式人生 > 其它 >Prism中Region(上)

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框架中由外到內,由總體到區域性一一分析從而使自己有一個更加清晰的認知。