Ribbon.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408
  1. // some code fetch from system.windows.controls.ribbon.
  2. using System;
  3. using System.Collections.ObjectModel;
  4. using System.Collections.Specialized;
  5. using System.Windows;
  6. using System.Windows.Controls;
  7. using System.Windows.Controls.Primitives;
  8. using System.Windows.Input;
  9. using HandyControl.Data;
  10. using HandyControl.Interactivity;
  11. using HandyControl.Tools.Extension;
  12. namespace HandyControl.Controls
  13. {
  14. // TODO: this control is a work-in-progress, it needs to support things like automatic resizing.
  15. [TemplatePart(Name = TabHeaderItemsControl, Type = typeof(ItemsControl))]
  16. [TemplatePart(Name = RootPanel, Type = typeof(Panel))]
  17. [TemplatePart(Name = ContentPanel, Type = typeof(Panel))]
  18. public class Ribbon : Selector
  19. {
  20. private const string TabHeaderItemsControl = "PART_TabHeaderItemsControl";
  21. private const string RootPanel = "PART_RootPanel";
  22. private const string ContentPanel = "PART_ContentPanel";
  23. private ItemsControl _tabHeaderItemsControl;
  24. private Panel _rootPanel;
  25. private Panel _contentPanel;
  26. private System.Windows.Window _window;
  27. private readonly ObservableCollection<object> _tabHeaderItemsSource = new();
  28. internal ItemsControl RibbonTabHeaderItemsControl => _tabHeaderItemsControl;
  29. public Ribbon()
  30. {
  31. SetRibbon(this, this);
  32. Loaded += Ribbon_Loaded;
  33. Unloaded += Ribbon_Unloaded;
  34. ItemContainerGenerator.StatusChanged += OnItemContainerGeneratorStatusChanged;
  35. CommandBindings.Add(new CommandBinding(ControlCommands.Switch, IsMinimizedSwitchButton_OnClick));
  36. }
  37. internal static readonly DependencyProperty RibbonProperty = DependencyProperty.RegisterAttached(
  38. "Ribbon", typeof(Ribbon), typeof(Ribbon),
  39. new FrameworkPropertyMetadata(default(Ribbon), FrameworkPropertyMetadataOptions.Inherits));
  40. internal static void SetRibbon(DependencyObject element, Ribbon value)
  41. => element.SetValue(RibbonProperty, value);
  42. internal static Ribbon GetRibbon(DependencyObject element)
  43. => (Ribbon) element.GetValue(RibbonProperty);
  44. public static readonly DependencyProperty IsDropDownOpenProperty = DependencyProperty.Register(
  45. nameof(IsDropDownOpen), typeof(bool), typeof(Ribbon), new PropertyMetadata(ValueBoxes.TrueBox, OnIsDropDownOpenChanged));
  46. private static void OnIsDropDownOpenChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
  47. {
  48. ((Ribbon) d).OnIsDropDownOpenChanged((bool) e.NewValue);
  49. }
  50. private void OnIsDropDownOpenChanged(bool isDropDownOpen)
  51. {
  52. if (_contentPanel == null)
  53. {
  54. return;
  55. }
  56. SwitchCurrentTabContentVisibility(isDropDownOpen);
  57. _contentPanel.Show(isDropDownOpen);
  58. }
  59. public bool IsDropDownOpen
  60. {
  61. get => (bool) GetValue(IsDropDownOpenProperty);
  62. set => SetValue(IsDropDownOpenProperty, value);
  63. }
  64. public static readonly DependencyProperty IsMinimizedProperty = DependencyProperty.Register(
  65. nameof(IsMinimized), typeof(bool), typeof(Ribbon), new PropertyMetadata(ValueBoxes.FalseBox));
  66. public bool IsMinimized
  67. {
  68. get => (bool) GetValue(IsMinimizedProperty);
  69. set => SetValue(IsMinimizedProperty, value);
  70. }
  71. public static readonly DependencyProperty ContentHeightProperty = DependencyProperty.Register(
  72. nameof(ContentHeight), typeof(double), typeof(Ribbon), new PropertyMetadata(default(double)));
  73. public double ContentHeight
  74. {
  75. get => (double) GetValue(ContentHeightProperty);
  76. set => SetValue(ContentHeightProperty, value);
  77. }
  78. public static readonly DependencyProperty PrefixContentProperty = DependencyProperty.Register(
  79. nameof(PrefixContent), typeof(object), typeof(Ribbon), new PropertyMetadata(default(object)));
  80. public object PrefixContent
  81. {
  82. get => GetValue(PrefixContentProperty);
  83. set => SetValue(PrefixContentProperty, value);
  84. }
  85. public static readonly DependencyProperty PostfixContentProperty = DependencyProperty.Register(
  86. nameof(PostfixContent), typeof(object), typeof(Ribbon), new PropertyMetadata(default(object)));
  87. public object PostfixContent
  88. {
  89. get => GetValue(PostfixContentProperty);
  90. set => SetValue(PostfixContentProperty, value);
  91. }
  92. public override void OnApplyTemplate()
  93. {
  94. base.OnApplyTemplate();
  95. _tabHeaderItemsControl = GetTemplateChild(TabHeaderItemsControl) as ItemsControl;
  96. if (_tabHeaderItemsControl is { ItemsSource: null })
  97. {
  98. _tabHeaderItemsControl.ItemsSource = _tabHeaderItemsSource;
  99. }
  100. _rootPanel = GetTemplateChild(RootPanel) as Panel;
  101. _contentPanel = GetTemplateChild(ContentPanel) as Panel;
  102. if (IsMinimized)
  103. {
  104. _rootPanel.SetCurrentValue(HeightProperty, _tabHeaderItemsControl.ActualHeight);
  105. }
  106. if (!IsDropDownOpen)
  107. {
  108. _contentPanel.SetCurrentValue(HeightProperty, .0);
  109. }
  110. }
  111. internal void ResetSelection()
  112. {
  113. SelectedIndex = -1;
  114. InitializeSelection();
  115. }
  116. internal void NotifyMouseClickedOnTabHeader(RibbonTabHeader tabHeader, MouseButtonEventArgs e)
  117. {
  118. if (_tabHeaderItemsControl == null)
  119. {
  120. return;
  121. }
  122. var selectedIndex = _tabHeaderItemsControl.ItemContainerGenerator.IndexFromContainer(tabHeader);
  123. if (e.ClickCount == 1)
  124. {
  125. var currentSelectedIndex = SelectedIndex;
  126. if (currentSelectedIndex < 0 || currentSelectedIndex != selectedIndex)
  127. {
  128. SelectedIndex = selectedIndex;
  129. if (IsMinimized)
  130. {
  131. IsDropDownOpen = true;
  132. }
  133. }
  134. else
  135. {
  136. if (IsMinimized)
  137. {
  138. IsDropDownOpen = !IsDropDownOpen;
  139. if (!IsDropDownOpen)
  140. {
  141. SelectedIndex = -1;
  142. }
  143. }
  144. }
  145. }
  146. else if (e.ClickCount == 2)
  147. {
  148. IsMinimized = !IsMinimized;
  149. IsDropDownOpen = !IsMinimized;
  150. if (IsMinimized && !IsDropDownOpen)
  151. {
  152. SelectedIndex = -1;
  153. }
  154. else
  155. {
  156. SelectedIndex = selectedIndex;
  157. }
  158. }
  159. }
  160. internal void NotifyTabHeaderChanged()
  161. {
  162. if (ItemContainerGenerator.Status != GeneratorStatus.ContainersGenerated)
  163. {
  164. return;
  165. }
  166. RefreshHeaderCollection();
  167. }
  168. protected override void OnItemsChanged(NotifyCollectionChangedEventArgs e)
  169. {
  170. base.OnItemsChanged(e);
  171. if (e.Action == NotifyCollectionChangedAction.Remove ||
  172. e.Action == NotifyCollectionChangedAction.Replace ||
  173. e.Action == NotifyCollectionChangedAction.Reset)
  174. {
  175. InitializeSelection();
  176. }
  177. if ((ItemContainerGenerator.Status != GeneratorStatus.ContainersGenerated ||
  178. e.Action != NotifyCollectionChangedAction.Move) && e.Action != NotifyCollectionChangedAction.Remove)
  179. {
  180. return;
  181. }
  182. RefreshHeaderCollection();
  183. }
  184. protected override DependencyObject GetContainerForItemOverride() => new RibbonTab();
  185. protected override bool IsItemItsOwnContainerOverride(object item) => item is RibbonTab;
  186. protected override void OnSelectionChanged(SelectionChangedEventArgs e)
  187. {
  188. base.OnSelectionChanged(e);
  189. if (e.AddedItems is not { Count: > 0 })
  190. {
  191. return;
  192. }
  193. SelectedItem = e.AddedItems[0];
  194. }
  195. private void OnPreviewMouseButton(MouseButtonEventArgs e)
  196. {
  197. var postion = e.GetPosition(this);
  198. if (InputHitTest(postion) == null)
  199. {
  200. if (IsMinimized && IsDropDownOpen)
  201. {
  202. IsDropDownOpen = false;
  203. SelectedIndex = -1;
  204. }
  205. }
  206. }
  207. private void Ribbon_Loaded(object sender, RoutedEventArgs e)
  208. {
  209. _window = System.Windows.Window.GetWindow(this);
  210. if (_window != null)
  211. {
  212. _window.Deactivated += Window_Deactivated;
  213. _window.PreviewMouseLeftButtonDown += Window_PreviewMouseLeftButtonDown;
  214. _window.PreviewMouseLeftButtonUp += Window_PreviewMouseLeftButtonUp;
  215. _window.PreviewMouseRightButtonDown += Window_PreviewMouseRightButtonDown;
  216. _window.PreviewMouseRightButtonUp += Window_PreviewMouseRightButtonUp;
  217. }
  218. }
  219. private void Ribbon_Unloaded(object sender, RoutedEventArgs e)
  220. {
  221. if (_window != null)
  222. {
  223. _window.Deactivated -= Window_Deactivated;
  224. }
  225. }
  226. private void Window_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e) => OnPreviewMouseButton(e);
  227. private void Window_PreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e) => OnPreviewMouseButton(e);
  228. private void Window_PreviewMouseRightButtonDown(object sender, MouseButtonEventArgs e) => OnPreviewMouseButton(e);
  229. private void Window_PreviewMouseRightButtonUp(object sender, MouseButtonEventArgs e) => OnPreviewMouseButton(e);
  230. private void Window_Deactivated(object sender, EventArgs e)
  231. {
  232. if (IsMinimized && IsDropDownOpen)
  233. {
  234. IsDropDownOpen = false;
  235. SelectedIndex = -1;
  236. }
  237. }
  238. private void IsMinimizedSwitchButton_OnClick(object sender, ExecutedRoutedEventArgs e)
  239. {
  240. IsDropDownOpen = !IsMinimized;
  241. if (!IsDropDownOpen)
  242. {
  243. SelectedIndex = -1;
  244. }
  245. }
  246. private void OnItemContainerGeneratorStatusChanged(object sender, EventArgs e)
  247. {
  248. if (ItemContainerGenerator.Status != GeneratorStatus.ContainersGenerated)
  249. {
  250. return;
  251. }
  252. InitializeSelection();
  253. RefreshHeaderCollection();
  254. }
  255. private int GetFirstVisibleTabIndex()
  256. {
  257. var count = Items.Count;
  258. for (var index = 0; index < count; ++index)
  259. {
  260. if (ItemContainerGenerator.ContainerFromIndex(index) is RibbonTab { IsVisible: true })
  261. {
  262. return index;
  263. }
  264. }
  265. return -1;
  266. }
  267. private void SwitchCurrentTabContentVisibility(bool isVisible)
  268. {
  269. var tab = GetCurrentTab();
  270. tab?.SwitchContentVisibility(isVisible);
  271. }
  272. private RibbonTab GetCurrentTab()
  273. {
  274. var index = SelectedIndex;
  275. if (index == -1)
  276. {
  277. return null;
  278. }
  279. if (ItemContainerGenerator.ContainerFromIndex(index) is RibbonTab ribbonTab)
  280. {
  281. return ribbonTab;
  282. }
  283. return null;
  284. }
  285. private void InitializeSelection()
  286. {
  287. if (!IsDropDownOpen)
  288. {
  289. SelectedIndex = -1;
  290. return;
  291. }
  292. if (SelectedIndex >= 0 || Items.Count <= 0)
  293. {
  294. return;
  295. }
  296. var firstVisibleTabIndex = GetFirstVisibleTabIndex();
  297. if (firstVisibleTabIndex < 0)
  298. {
  299. return;
  300. }
  301. SelectedIndex = firstVisibleTabIndex;
  302. }
  303. private void RefreshHeaderCollection()
  304. {
  305. var itemsCount = Items.Count;
  306. for (var index = 0; index < itemsCount; ++index)
  307. {
  308. object header = null;
  309. if (ItemContainerGenerator.ContainerFromIndex(index) is RibbonTab ribbonTab)
  310. {
  311. header = ribbonTab.Header;
  312. }
  313. header ??= string.Empty;
  314. if (index >= _tabHeaderItemsSource.Count)
  315. {
  316. _tabHeaderItemsSource.Add(header);
  317. }
  318. else
  319. {
  320. _tabHeaderItemsSource[index] = header;
  321. }
  322. }
  323. var headerCount = _tabHeaderItemsSource.Count;
  324. for (var index = 0; index < headerCount - itemsCount; ++index)
  325. {
  326. _tabHeaderItemsSource.RemoveAt(itemsCount);
  327. }
  328. }
  329. }
  330. }