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