PageRenderTime 49ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/V2.2/trunk/CAL/Silverlight/Composite.Presentation/Regions/Behaviors/TabControlRegionSyncBehavior.cs

#
C# | 263 lines | 177 code | 27 blank | 59 comment | 36 complexity | b1d0a1bef2313bfbeb253212b31e18ee 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.Generic;
  19. using System.Collections.Specialized;
  20. using System.Windows;
  21. using System.Windows.Controls;
  22. using Microsoft.Practices.Composite.Presentation.Properties;
  23. using Microsoft.Practices.Composite.Regions;
  24. namespace Microsoft.Practices.Composite.Presentation.Regions.Behaviors
  25. {
  26. /// <summary>
  27. /// Behavior that generates <see cref="TabItem"/> containers for the added items
  28. /// and also keeps the <see cref="TabControl.SelectedItem"/> and the <see cref="IRegion.ActiveViews"/> in sync.
  29. /// </summary>
  30. public class TabControlRegionSyncBehavior : RegionBehavior, IHostAwareRegionBehavior
  31. {
  32. ///<summary>
  33. /// The behavior key for this region sync behavior.
  34. ///</summary>
  35. public const string BehaviorKey = "TabControlRegionSyncBehavior";
  36. private static readonly DependencyProperty IsGeneratedProperty =
  37. DependencyProperty.RegisterAttached("IsGenerated", typeof(bool), typeof(TabControlRegionSyncBehavior), null);
  38. private TabControl hostControl;
  39. /// <summary>
  40. /// Gets or sets the <see cref="DependencyObject"/> that the <see cref="IRegion"/> is attached to.
  41. /// </summary>
  42. /// <value>A <see cref="DependencyObject"/> that the <see cref="IRegion"/> is attached to.
  43. /// This is usually a <see cref="FrameworkElement"/> that is part of the tree.</value>
  44. public DependencyObject HostControl
  45. {
  46. get
  47. {
  48. return this.hostControl;
  49. }
  50. set
  51. {
  52. TabControl newValue = value as TabControl;
  53. if (newValue == null)
  54. {
  55. throw new InvalidOperationException(Resources.HostControlMustBeATabControl);
  56. }
  57. if (IsAttached)
  58. {
  59. throw new InvalidOperationException(Resources.HostControlCannotBeSetAfterAttach);
  60. }
  61. this.hostControl = newValue;
  62. }
  63. }
  64. /// <summary>
  65. /// Override this method to perform the logic after the behavior has been attached.
  66. /// </summary>
  67. protected override void OnAttach()
  68. {
  69. if (this.hostControl == null)
  70. {
  71. throw new InvalidOperationException(Resources.HostControlCannotBeNull);
  72. }
  73. this.SynchronizeItems();
  74. this.hostControl.SelectionChanged += this.OnSelectionChanged;
  75. this.Region.ActiveViews.CollectionChanged += this.OnActiveViewsChanged;
  76. this.Region.Views.CollectionChanged += this.OnViewsChanged;
  77. }
  78. /// <summary>
  79. /// Gets the item contained in the <see cref="TabItem"/>.
  80. /// </summary>
  81. /// <param name="tabItem">The container item.</param>
  82. /// <returns>The item contained in the <paramref name="tabItem"/> if it was generated automatically by the behavior; otherwise <paramref name="tabItem"/>.</returns>
  83. protected virtual object GetContainedItem(TabItem tabItem)
  84. {
  85. if ((bool)tabItem.GetValue(IsGeneratedProperty))
  86. {
  87. return tabItem.Content;
  88. }
  89. return tabItem;
  90. }
  91. /// <summary>
  92. /// Override to change how TabItem's are prepared for items.
  93. /// </summary>
  94. /// <param name="item">The item to wrap in a TabItem</param>
  95. /// <param name="parent">The parent <see cref="DependencyObject"/></param>
  96. /// <returns>A tab item that wraps the supplied <paramref name="item"/></returns>
  97. protected virtual TabItem PrepareContainerForItem(object item, DependencyObject parent)
  98. {
  99. TabItem container = item as TabItem;
  100. if (container == null)
  101. {
  102. object dataContext = GetDataContext(item);
  103. container = new TabItem();
  104. container.Content = item;
  105. container.Style = TabControlRegionAdapter.GetItemContainerStyle(parent);
  106. container.DataContext = dataContext; // To run with SL 2
  107. container.Header = dataContext; // To run with SL 3
  108. container.SetValue(IsGeneratedProperty, true);
  109. }
  110. return container;
  111. }
  112. /// <summary>
  113. /// Undoes the effects of the <see cref="PrepareContainerForItem"/> method.
  114. /// </summary>
  115. /// <param name="tabItem">The container element for the item.</param>
  116. protected virtual void ClearContainerForItem(TabItem tabItem)
  117. {
  118. if ((bool)tabItem.GetValue(IsGeneratedProperty))
  119. {
  120. tabItem.Content = null;
  121. }
  122. }
  123. /// <summary>
  124. /// Creates or identifies the element that is used to display the given item.
  125. /// </summary>
  126. /// <param name="item">The item to get the container for.</param>
  127. /// <param name="itemCollection">The parent's <see cref="ItemCollection"/>.</param>
  128. /// <returns>The element that is used to display the given item.</returns>
  129. protected virtual TabItem GetContainerForItem(object item, ItemCollection itemCollection)
  130. {
  131. TabItem container = item as TabItem;
  132. if (container != null && ((bool)container.GetValue(IsGeneratedProperty)) == false)
  133. {
  134. return container;
  135. }
  136. foreach (TabItem tabItem in itemCollection)
  137. {
  138. if ((bool)tabItem.GetValue(IsGeneratedProperty))
  139. {
  140. if (tabItem.Content == item)
  141. {
  142. return tabItem;
  143. }
  144. }
  145. }
  146. return null;
  147. }
  148. /// <summary>
  149. /// Return the appropriate data context. If the item is a FrameworkElement it cannot be a data context in Silverlight, so we use its data context.
  150. /// Otherwise, we just us the item as the data context.
  151. /// </summary>
  152. private static object GetDataContext(object item)
  153. {
  154. FrameworkElement frameworkElement = item as FrameworkElement;
  155. return frameworkElement == null ? item : frameworkElement.DataContext;
  156. }
  157. private void SynchronizeItems()
  158. {
  159. List<object> existingItems = new List<object>();
  160. if (this.hostControl.Items.Count > 0)
  161. {
  162. // Control must be empty before "Binding" to a region
  163. foreach (object childItem in this.hostControl.Items)
  164. {
  165. existingItems.Add(childItem);
  166. }
  167. }
  168. foreach (object view in this.Region.Views)
  169. {
  170. TabItem tabItem = this.PrepareContainerForItem(view, this.hostControl);
  171. this.hostControl.Items.Add(tabItem);
  172. }
  173. foreach (object existingItem in existingItems)
  174. {
  175. this.Region.Add(existingItem);
  176. }
  177. }
  178. private void OnSelectionChanged(object sender, SelectionChangedEventArgs e)
  179. {
  180. // e.OriginalSource == null, that's why we use sender.
  181. if (this.hostControl == sender)
  182. {
  183. foreach (TabItem tabItem in e.RemovedItems)
  184. {
  185. object item = this.GetContainedItem(tabItem);
  186. // check if the view is in both Views and ActiveViews collections (there may be out of sync)
  187. if (this.Region.Views.Contains(item) && this.Region.ActiveViews.Contains(item))
  188. {
  189. this.Region.Deactivate(item);
  190. }
  191. }
  192. foreach (TabItem tabItem in e.AddedItems)
  193. {
  194. object item = this.GetContainedItem(tabItem);
  195. if (!this.Region.ActiveViews.Contains(item))
  196. {
  197. this.Region.Activate(item);
  198. }
  199. }
  200. }
  201. }
  202. private void OnActiveViewsChanged(object sender, NotifyCollectionChangedEventArgs e)
  203. {
  204. if (e.Action == NotifyCollectionChangedAction.Add)
  205. {
  206. this.hostControl.SelectedItem = this.GetContainerForItem(e.NewItems[0], this.hostControl.Items);
  207. }
  208. else if (e.Action == NotifyCollectionChangedAction.Remove
  209. && this.hostControl.SelectedItem != null
  210. && e.OldItems.Contains(this.GetContainedItem((TabItem)this.hostControl.SelectedItem)))
  211. {
  212. this.hostControl.SelectedItem = null;
  213. }
  214. }
  215. private void OnViewsChanged(object sender, NotifyCollectionChangedEventArgs e)
  216. {
  217. if (e.Action == NotifyCollectionChangedAction.Add)
  218. {
  219. foreach (object newItem in e.NewItems)
  220. {
  221. TabItem tabItem = this.PrepareContainerForItem(newItem, this.hostControl);
  222. this.hostControl.Items.Add(tabItem);
  223. }
  224. }
  225. else if (e.Action == NotifyCollectionChangedAction.Remove)
  226. {
  227. foreach (object oldItem in e.OldItems)
  228. {
  229. TabItem tabItem = this.GetContainerForItem(oldItem, this.hostControl.Items);
  230. this.hostControl.Items.Remove(tabItem);
  231. this.ClearContainerForItem(tabItem);
  232. }
  233. }
  234. }
  235. }
  236. }