PageRenderTime 35ms CodeModel.GetById 12ms app.highlight 18ms RepoModel.GetById 1ms app.codeStats 1ms

/V4/PrismLibrary/Desktop/Prism/Regions/Region.cs

#
C# | 474 lines | 310 code | 55 blank | 109 comment | 60 complexity | 451b3a154a3dbcc4faa0b6a5634a8f6f MD5 | raw file
  1//===================================================================================
  2// Microsoft patterns & practices
  3// Composite Application Guidance for Windows Presentation Foundation and Silverlight
  4//===================================================================================
  5// Copyright (c) Microsoft Corporation.  All rights reserved.
  6// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY
  7// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT
  8// LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
  9// FITNESS FOR A PARTICULAR PURPOSE.
 10//===================================================================================
 11// The example companies, organizations, products, domain names,
 12// e-mail addresses, logos, people, places, and events depicted
 13// herein are fictitious.  No association with any real company,
 14// organization, product, domain name, email address, logo, person,
 15// places, or events is intended or should be inferred.
 16//===================================================================================
 17using System;
 18using System.Collections.ObjectModel;
 19using System.ComponentModel;
 20using System.Diagnostics.CodeAnalysis;
 21using System.Globalization;
 22using System.Linq;
 23using System.Windows;
 24using Microsoft.Practices.Prism.Properties;
 25using Microsoft.Practices.ServiceLocation;
 26
 27namespace Microsoft.Practices.Prism.Regions
 28{
 29    /// <summary>
 30    /// Implementation of <see cref="IRegion"/> that allows multiple active views.
 31    /// </summary>
 32    public class Region : IRegion
 33    {
 34        private ObservableCollection<ItemMetadata> itemMetadataCollection;
 35        private string name;
 36        private ViewsCollection views;
 37        private ViewsCollection activeViews;
 38        private object context;
 39        private IRegionManager regionManager;
 40        private IRegionNavigationService regionNavigationService;
 41
 42        private Comparison<object> sort;
 43
 44        /// <summary>
 45        /// Initializes a new instance of <see cref="Region"/>.
 46        /// </summary>
 47        public Region()
 48        {
 49            this.Behaviors = new RegionBehaviorCollection(this);
 50
 51            this.sort = Region.DefaultSortComparison;
 52        }
 53
 54        /// <summary>
 55        /// Occurs when a property value changes.
 56        /// </summary>
 57        public event PropertyChangedEventHandler PropertyChanged;
 58
 59        /// <summary>
 60        /// Gets the collection of <see cref="IRegionBehavior"/>s that can extend the behavior of regions. 
 61        /// </summary>
 62        public IRegionBehaviorCollection Behaviors { get; private set; }
 63
 64        /// <summary>
 65        /// Gets or sets a context for the region. This value can be used by the user to share context with the views.
 66        /// </summary>
 67        /// <value>The context value to be shared.</value>
 68        public object Context
 69        {
 70            get
 71            {
 72                return this.context;
 73            }
 74
 75            set
 76            {
 77                if (this.context != value)
 78                {
 79                    this.context = value;
 80                    this.OnPropertyChanged("Context");
 81                }
 82            }
 83        }
 84
 85        /// <summary>
 86        /// Gets the name of the region that uniequely identifies the region within a <see cref="IRegionManager"/>.
 87        /// </summary>
 88        /// <value>The name of the region.</value>
 89        public string Name
 90        {
 91            get
 92            {
 93                return this.name;
 94            }
 95
 96            set
 97            {
 98                if (this.name != null && this.name != value)
 99                {
100                    throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, Resources.CannotChangeRegionNameException, this.name));
101                }
102
103                if (string.IsNullOrEmpty(value))
104                {
105                    throw new ArgumentException(Resources.RegionNameCannotBeEmptyException);
106                }
107
108                this.name = value;
109                this.OnPropertyChanged("Name");
110            }
111        }
112
113        /// <summary>
114        /// Gets a readonly view of the collection of views in the region.
115        /// </summary>
116        /// <value>An <see cref="IViewsCollection"/> of all the added views.</value>
117        public virtual IViewsCollection Views
118        {
119            get
120            {
121                if (this.views == null)
122                {
123                    this.views = new ViewsCollection(ItemMetadataCollection, x => true);
124                    this.views.SortComparison = this.sort;
125                }
126
127                return this.views;
128            }
129        }
130
131        /// <summary>
132        /// Gets a readonly view of the collection of all the active views in the region.
133        /// </summary>
134        /// <value>An <see cref="IViewsCollection"/> of all the active views.</value>
135        public virtual IViewsCollection ActiveViews
136        {
137            get
138            {
139                if (this.activeViews == null)
140                {
141                    this.activeViews = new ViewsCollection(ItemMetadataCollection, x => x.IsActive);
142                    this.activeViews.SortComparison = this.sort;
143                }
144
145                return this.activeViews;
146            }
147        }
148
149        /// <summary>
150        /// Gets or sets the comparison used to sort the views.
151        /// </summary>
152        /// <value>The comparison to use.</value>
153        public Comparison<object> SortComparison
154        {
155            get
156            {
157                return this.sort;
158            }
159            set
160            {
161                this.sort = value;
162
163                if (this.activeViews != null)
164                {
165                    this.activeViews.SortComparison = this.sort;
166                }
167
168                if (this.views != null)
169                {
170                    this.views.SortComparison = this.sort;
171                }
172            }
173        }
174
175        /// <summary>
176        /// 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" />.
177        /// </summary>
178        /// <value>The <see cref="IRegionManager"/> where this <see cref="IRegion"/> is registered.</value>
179        /// <remarks>This is usually used by implementations of <see cref="IRegionManager"/> and should not be
180        /// used by the developer explicitely.</remarks>
181        public IRegionManager RegionManager
182        {
183            get
184            {
185                return this.regionManager;
186            }
187
188            set
189            {
190                if (this.regionManager != value)
191                {
192                    this.regionManager = value;
193                    this.OnPropertyChanged("RegionManager");
194                }
195            }
196        }
197
198        /// <summary>
199        /// Gets the navigation service.
200        /// </summary>
201        /// <value>The navigation service.</value>
202        public IRegionNavigationService NavigationService
203        {
204            get
205            {
206                if (this.regionNavigationService == null)
207                {
208                    this.regionNavigationService = ServiceLocator.Current.GetInstance<IRegionNavigationService>();
209                    this.regionNavigationService.Region = this;
210                }
211
212                return this.regionNavigationService;
213            }
214
215            set
216            {
217                this.regionNavigationService = value;
218            }
219        }
220
221        /// <summary>
222        /// Gets the collection with all the views along with their metadata.
223        /// </summary>
224        /// <value>An <see cref="ObservableCollection{T}"/> of <see cref="ItemMetadata"/> with all the added views.</value>
225        protected virtual ObservableCollection<ItemMetadata> ItemMetadataCollection
226        {
227            get
228            {
229                if (this.itemMetadataCollection == null)
230                {
231                    this.itemMetadataCollection = new ObservableCollection<ItemMetadata>();
232                }
233
234                return this.itemMetadataCollection;
235            }
236        }
237
238        /// <overloads>Adds a new view to the region.</overloads>
239        /// <summary>
240        /// Adds a new view to the region.
241        /// </summary>
242        /// <param name="view">The view to add.</param>
243        /// <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>
244        public IRegionManager Add(object view)
245        {
246            return this.Add(view, null, false);
247        }
248
249        /// <summary>
250        /// Adds a new view to the region.
251        /// </summary>
252        /// <param name="view">The view to add.</param>
253        /// <param name="viewName">The name of the view. This can be used to retrieve it later by calling <see cref="IRegion.GetView"/>.</param>
254        /// <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>
255        public IRegionManager Add(object view, string viewName)
256        {
257            if (string.IsNullOrEmpty(viewName))
258            {
259                throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, Resources.StringCannotBeNullOrEmpty, "viewName"));
260            }
261
262            return this.Add(view, viewName, false);
263        }
264
265        /// <summary>
266        /// Adds a new view to the region.
267        /// </summary>
268        /// <param name="view">The view to add.</param>
269        /// <param name="viewName">The name of the view. This can be used to retrieve it later by calling <see cref="IRegion.GetView"/>.</param>
270        /// <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>
271        /// <returns>The <see cref="IRegionManager"/> that is set on the view if it is a <see cref="DependencyObject"/>.</returns>
272        public virtual IRegionManager Add(object view, string viewName, bool createRegionManagerScope)
273        {
274            IRegionManager manager = createRegionManagerScope ? this.RegionManager.CreateRegionManager() : this.RegionManager;
275            this.InnerAdd(view, viewName, manager);
276            return manager;
277        }
278
279        /// <summary>
280        /// Removes the specified view from the region.
281        /// </summary>
282        /// <param name="view">The view to remove.</param>
283        public virtual void Remove(object view)
284        {
285            ItemMetadata itemMetadata = this.GetItemMetadataOrThrow(view);
286
287            this.ItemMetadataCollection.Remove(itemMetadata);
288
289            DependencyObject dependencyObject = view as DependencyObject;
290            if (dependencyObject != null && Regions.RegionManager.GetRegionManager(dependencyObject) == this.RegionManager)
291            {
292                dependencyObject.ClearValue(Regions.RegionManager.RegionManagerProperty);
293            }
294        }
295
296        /// <summary>
297        /// Marks the specified view as active. 
298        /// </summary>
299        /// <param name="view">The view to activate.</param>
300        public virtual void Activate(object view)
301        {
302            ItemMetadata itemMetadata = this.GetItemMetadataOrThrow(view);
303
304            if (!itemMetadata.IsActive)
305            {
306                itemMetadata.IsActive = true;
307            }
308        }
309
310        /// <summary>
311        /// Marks the specified view as inactive. 
312        /// </summary>
313        /// <param name="view">The view to deactivate.</param>
314        public virtual void Deactivate(object view)
315        {
316            ItemMetadata itemMetadata = this.GetItemMetadataOrThrow(view);
317
318            if (itemMetadata.IsActive)
319            {
320                itemMetadata.IsActive = false;
321            }
322        }
323
324        /// <summary>
325        /// Returns the view instance that was added to the region using a specific name.
326        /// </summary>
327        /// <param name="viewName">The name used when adding the view to the region.</param>
328        /// <returns>Returns the named view or <see langword="null"/> if the view with <paramref name="viewName"/> does not exist in the current region.</returns>
329        public virtual object GetView(string viewName)
330        {
331            if (string.IsNullOrEmpty(viewName))
332            {
333                throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, Resources.StringCannotBeNullOrEmpty, "viewName"));
334            }
335
336            ItemMetadata metadata = this.ItemMetadataCollection.FirstOrDefault(x => x.Name == viewName);
337
338            if (metadata != null)
339            {
340                return metadata.Item;
341            }
342
343            return null;
344        }
345
346        /// <summary>
347        /// Initiates navigation to the specified target.
348        /// </summary>
349        /// <param name="target">The target.</param>
350        /// <param name="navigationCallback">A callback to execute when the navigation request is completed.</param>
351        public void RequestNavigate(Uri target, Action<NavigationResult> navigationCallback)
352        {
353            this.NavigationService.RequestNavigate(target, navigationCallback);
354        }
355
356        private void InnerAdd(object view, string viewName, IRegionManager scopedRegionManager)
357        {
358            if (this.ItemMetadataCollection.FirstOrDefault(x => x.Item == view) != null)
359            {
360                throw new InvalidOperationException(Resources.RegionViewExistsException);
361            }
362
363            ItemMetadata itemMetadata = new ItemMetadata(view);
364            if (!string.IsNullOrEmpty(viewName))
365            {
366                if (this.ItemMetadataCollection.FirstOrDefault(x => x.Name == viewName) != null)
367                {
368                    throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, Resources.RegionViewNameExistsException, viewName));
369                }
370                itemMetadata.Name = viewName;
371            }
372
373            DependencyObject dependencyObject = view as DependencyObject;
374
375            if (dependencyObject != null)
376            {
377                Regions.RegionManager.SetRegionManager(dependencyObject, scopedRegionManager);
378            }
379
380            this.ItemMetadataCollection.Add(itemMetadata);
381        }
382
383        private ItemMetadata GetItemMetadataOrThrow(object view)
384        {
385            if (view == null)
386            {
387                throw new ArgumentNullException("view");
388            }
389
390            ItemMetadata itemMetadata = this.ItemMetadataCollection.FirstOrDefault(x => x.Item == view);
391            if (itemMetadata == null)
392            {
393                throw new ArgumentException(Resources.ViewNotInRegionException, "view");
394            }
395
396            return itemMetadata;
397        }
398
399        private void OnPropertyChanged(string propertyName)
400        {
401            PropertyChangedEventHandler eventHandler = this.PropertyChanged;
402            if (eventHandler != null)
403            {
404                eventHandler(this, new PropertyChangedEventArgs(propertyName));
405            }
406        }
407
408        /// <summary>
409        /// The default sort algorithm.
410        /// </summary>
411        /// <param name="x">The first view to compare.</param>
412        /// <param name="y">The second view to compare.</param>
413        /// <returns></returns>
414        [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "y")]
415        [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "x")]
416        public static int DefaultSortComparison(object x, object y)
417        {
418            if (x == null)
419            {
420                if (y == null)
421                {
422                    return 0;
423                }
424                else
425                {
426                    return -1;
427                }
428            }
429            else
430            {
431                if (y == null)
432                {
433                    return 1;
434                }
435                else
436                {
437                    Type xType = x.GetType();
438                    Type yType = y.GetType();
439
440                    ViewSortHintAttribute xAttribute = xType.GetCustomAttributes(typeof(ViewSortHintAttribute), true).FirstOrDefault() as ViewSortHintAttribute;
441                    ViewSortHintAttribute yAttribute = yType.GetCustomAttributes(typeof(ViewSortHintAttribute), true).FirstOrDefault() as ViewSortHintAttribute;
442
443                    return ViewSortHintAttributeComparison(xAttribute, yAttribute);
444                }
445            }
446        }
447
448        private static int ViewSortHintAttributeComparison(ViewSortHintAttribute x, ViewSortHintAttribute y)
449        {
450            if (x == null)
451            {
452                if (y == null)
453                {
454                    return 0;
455                }
456                else
457                {
458                    return -1;
459                }
460            }
461            else
462            {
463                if (y == null)
464                {
465                    return 1;
466                }
467                else
468                {
469                    return string.Compare(x.Hint, y.Hint, StringComparison.Ordinal);
470                }
471            }
472        }
473    }
474}