PageRenderTime 52ms CodeModel.GetById 28ms RepoModel.GetById 1ms app.codeStats 0ms

/cinch/V1 (VS2008 WPF Only)/cinch/MVVM.Demo/UserControls/TabControlEx.cs

#
C# | 238 lines | 155 code | 27 blank | 56 comment | 28 complexity | d3d889a58124e907d90d4bfc65002fca MD5 | raw file
  1. using System;
  2. using System.Collections.Specialized;
  3. using System.Windows;
  4. using System.Windows.Controls;
  5. using System.Windows.Controls.Primitives;
  6. namespace MVVM.Demo
  7. {
  8. /// <summary>
  9. /// The standard WPF TabControl is quite bad in the fact that it only
  10. /// even contains the current TabItem in the VisualTree, so if you
  11. /// have complex views it takes a while to re-create the view each tab
  12. /// selection change.Which makes the standard TabControl very sticky to
  13. /// work with. This class along with its associated ControlTemplate
  14. /// allow all TabItems to remain in the VisualTree without it being Sticky.
  15. /// It does this by keeping all TabItem content in the VisualTree but
  16. /// hides all inactive TabItem content, and only keeps the active TabItem
  17. /// content shown.
  18. /// </summary>
  19. [TemplatePart(Name = "PART_ItemsHolder", Type = typeof(Panel))]
  20. public class TabControlEx : TabControl
  21. {
  22. #region Data
  23. private Panel itemsHolder = null;
  24. #endregion
  25. #region Ctor
  26. public TabControlEx()
  27. : base()
  28. {
  29. // this is necessary so that we get the initial databound selected item
  30. this.ItemContainerGenerator.StatusChanged += ItemContainerGenerator_StatusChanged;
  31. this.Loaded += TabControlEx_Loaded;
  32. }
  33. #endregion
  34. #region Public/Protected Methods
  35. /// <summary>
  36. /// get the ItemsHolder and generate any children
  37. /// </summary>
  38. public override void OnApplyTemplate()
  39. {
  40. base.OnApplyTemplate();
  41. itemsHolder = GetTemplateChild("PART_ItemsHolder") as Panel;
  42. UpdateSelectedItem();
  43. }
  44. /// <summary>
  45. /// when the items change we remove any generated panel children and add any new ones as necessary
  46. /// </summary>
  47. /// <param name="e"></param>
  48. protected override void OnItemsChanged(NotifyCollectionChangedEventArgs e)
  49. {
  50. base.OnItemsChanged(e);
  51. if (itemsHolder == null)
  52. {
  53. return;
  54. }
  55. switch (e.Action)
  56. {
  57. case NotifyCollectionChangedAction.Reset:
  58. itemsHolder.Children.Clear();
  59. break;
  60. case NotifyCollectionChangedAction.Add:
  61. case NotifyCollectionChangedAction.Remove:
  62. if (e.OldItems != null)
  63. {
  64. foreach (var item in e.OldItems)
  65. {
  66. ContentPresenter cp = FindChildContentPresenter(item);
  67. if (cp != null)
  68. {
  69. itemsHolder.Children.Remove(cp);
  70. }
  71. }
  72. }
  73. // don't do anything with new items because we don't want to
  74. // create visuals that aren't being shown
  75. UpdateSelectedItem();
  76. break;
  77. case NotifyCollectionChangedAction.Replace:
  78. throw new NotImplementedException("Replace not implemented yet");
  79. }
  80. }
  81. /// <summary>
  82. /// update the visible child in the ItemsHolder
  83. /// </summary>
  84. /// <param name="e"></param>
  85. protected override void OnSelectionChanged(SelectionChangedEventArgs e)
  86. {
  87. base.OnSelectionChanged(e);
  88. UpdateSelectedItem();
  89. }
  90. /// <summary>
  91. /// copied from TabControl; wish it were protected in that class instead of private
  92. /// </summary>
  93. /// <returns></returns>
  94. protected TabItem GetSelectedTabItem()
  95. {
  96. object selectedItem = base.SelectedItem;
  97. if (selectedItem == null)
  98. {
  99. return null;
  100. }
  101. TabItem item = selectedItem as TabItem;
  102. if (item == null)
  103. {
  104. item = base.ItemContainerGenerator.ContainerFromIndex(base.SelectedIndex) as TabItem;
  105. }
  106. return item;
  107. }
  108. #endregion
  109. #region Private Methods
  110. /// <summary>
  111. /// in some scenarios we need to update when loaded in case the
  112. /// ApplyTemplate happens before the databind.
  113. /// </summary>
  114. /// <param name="sender"></param>
  115. /// <param name="e"></param>
  116. private void TabControlEx_Loaded(object sender, RoutedEventArgs e)
  117. {
  118. UpdateSelectedItem();
  119. }
  120. /// <summary>
  121. /// if containers are done, generate the selected item
  122. /// </summary>
  123. /// <param name="sender"></param>
  124. /// <param name="e"></param>
  125. private void ItemContainerGenerator_StatusChanged(object sender, EventArgs e)
  126. {
  127. if (this.ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated)
  128. {
  129. this.ItemContainerGenerator.StatusChanged -= ItemContainerGenerator_StatusChanged;
  130. UpdateSelectedItem();
  131. }
  132. }
  133. /// <summary>
  134. /// generate a ContentPresenter for the selected item
  135. /// </summary>
  136. private void UpdateSelectedItem()
  137. {
  138. if (itemsHolder == null)
  139. {
  140. return;
  141. }
  142. // generate a ContentPresenter if necessary
  143. TabItem item = GetSelectedTabItem();
  144. if (item != null)
  145. {
  146. CreateChildContentPresenter(item);
  147. }
  148. // show the right child
  149. foreach (ContentPresenter child in itemsHolder.Children)
  150. {
  151. child.Visibility = ((child.Tag as TabItem).IsSelected) ? Visibility.Visible : Visibility.Collapsed;
  152. }
  153. }
  154. /// <summary>
  155. /// create the child ContentPresenter for the given item (could be data or a TabItem)
  156. /// </summary>
  157. /// <param name="item"></param>
  158. /// <returns></returns>
  159. private ContentPresenter CreateChildContentPresenter(object item)
  160. {
  161. if (item == null)
  162. {
  163. return null;
  164. }
  165. ContentPresenter cp = FindChildContentPresenter(item);
  166. if (cp != null)
  167. {
  168. return cp;
  169. }
  170. // the actual child to be added. cp.Tag is a reference to the TabItem
  171. cp = new ContentPresenter();
  172. cp.Content = (item is TabItem) ? (item as TabItem).Content : item;
  173. cp.ContentTemplate = this.SelectedContentTemplate;
  174. cp.ContentTemplateSelector = this.SelectedContentTemplateSelector;
  175. cp.ContentStringFormat = this.SelectedContentStringFormat;
  176. cp.Visibility = Visibility.Collapsed;
  177. cp.Tag = (item is TabItem) ? item : (this.ItemContainerGenerator.ContainerFromItem(item));
  178. itemsHolder.Children.Add(cp);
  179. return cp;
  180. }
  181. /// <summary>
  182. /// Find the CP for the given object. data could be a TabItem or a piece of data
  183. /// </summary>
  184. /// <param name="data"></param>
  185. /// <returns></returns>
  186. private ContentPresenter FindChildContentPresenter(object data)
  187. {
  188. if (data is TabItem)
  189. {
  190. data = (data as TabItem).Content;
  191. }
  192. if (data == null)
  193. {
  194. return null;
  195. }
  196. if (itemsHolder == null)
  197. {
  198. return null;
  199. }
  200. foreach (ContentPresenter cp in itemsHolder.Children)
  201. {
  202. if (cp.Content == data)
  203. {
  204. return cp;
  205. }
  206. }
  207. return null;
  208. }
  209. #endregion
  210. }
  211. }