You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

421 lines
16 KiB

  1. using BPA.UIControl.Commons.KnownBoxes;
  2. using System;
  3. using System.Collections;
  4. using System.Windows;
  5. using System.Windows.Controls;
  6. using System.Windows.Controls.Primitives;
  7. using System.Windows.Media;
  8. namespace BPA.UIControl
  9. {
  10. /// <summary>
  11. /// 汉堡包菜单
  12. /// </summary>
  13. [StyleTypedProperty(Property = "ItemContainerStyle", StyleTargetType = typeof(HamburgerMenuItem))]
  14. [StyleTypedProperty(Property = "OptionsItemContainerStyle", StyleTargetType = typeof(HamburgerMenuOptionsItem))]
  15. [TemplatePart(Name = HamburgerButtonPartName, Type = typeof(Button))]
  16. [TemplatePart(Name = OptionsItemsControlPartName, Type = typeof(MenuBase))]
  17. [TemplatePart(Name = ContentTransitionPartName, Type = typeof(Transition))]
  18. public class HamburgerMenu : TabControl
  19. {
  20. /// <summary>
  21. /// 汉堡包按钮
  22. /// </summary>
  23. public const string HamburgerButtonPartName = "PART_HamburgerButton";
  24. /// <summary>
  25. /// 选项集合控件
  26. /// </summary>
  27. public const string OptionsItemsControlPartName = "PART_OptionsItemsControl";
  28. /// <summary>
  29. /// 内容转换
  30. /// </summary>
  31. public const string ContentTransitionPartName = "PART_ContentTransition";
  32. #region fields
  33. private CornerRadius _cornerRadius;
  34. private MenuBase optionsMenu;
  35. private Transition contentTransition;
  36. #endregion fields
  37. #region properties
  38. /// <summary>
  39. /// 圆角半径
  40. /// </summary>
  41. public static readonly DependencyProperty CornerRadiusProperty = Border.CornerRadiusProperty.AddOwner(typeof(HamburgerMenu), new FrameworkPropertyMetadata(default(CornerRadius), FrameworkPropertyMetadataOptions.AffectsMeasure));
  42. /// <summary>
  43. /// 圆角半径
  44. /// </summary>
  45. public CornerRadius CornerRadius
  46. {
  47. get => _cornerRadius;
  48. set => SetValue(CornerRadiusProperty, value);
  49. }
  50. /// <summary>
  51. /// 是否展开菜单
  52. /// </summary>
  53. public static readonly DependencyProperty IsExpandedProperty = DependencyProperty.Register(
  54. "IsExpanded", typeof(bool), typeof(HamburgerMenu), new FrameworkPropertyMetadata(BooleanBoxes.FalseBox, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
  55. /// <summary>
  56. /// 是否展开菜单
  57. /// </summary>
  58. public bool IsExpanded
  59. {
  60. get { return (bool)GetValue(IsExpandedProperty); }
  61. set { SetValue(IsExpandedProperty, BooleanBoxes.Box(value)); }
  62. }
  63. /// <summary>
  64. /// 标题
  65. /// </summary>
  66. public static readonly DependencyProperty HeaderProperty = DependencyProperty.Register(
  67. "Header", typeof(string), typeof(HamburgerMenu), new PropertyMetadata(default(string)));
  68. /// <summary>
  69. /// 标题
  70. /// </summary>
  71. public string Header
  72. {
  73. get { return (string)GetValue(HeaderProperty); }
  74. set { SetValue(HeaderProperty, value); }
  75. }
  76. /// <summary>
  77. /// 面板头部内容
  78. /// </summary>
  79. public static readonly DependencyProperty PaneHeaderProperty = DependencyProperty.Register(
  80. "PaneHeader", typeof(object), typeof(HamburgerMenu), new PropertyMetadata(default(object)));
  81. /// <summary>
  82. /// 面板头部内容
  83. /// </summary>
  84. public object PaneHeader
  85. {
  86. get { return (object)GetValue(PaneHeaderProperty); }
  87. set { SetValue(PaneHeaderProperty, value); }
  88. }
  89. /// <summary>
  90. /// 面板底部内容
  91. /// </summary>
  92. public static readonly DependencyProperty PaneFooterProperty = DependencyProperty.Register(
  93. "PaneFooter", typeof(object), typeof(HamburgerMenu), new PropertyMetadata(default(object)));
  94. /// <summary>
  95. /// 面板底部内容
  96. /// </summary>
  97. public object PaneFooter
  98. {
  99. get { return (object)GetValue(PaneFooterProperty); }
  100. set { SetValue(PaneFooterProperty, value); }
  101. }
  102. /// <summary>
  103. /// 折叠宽度
  104. /// </summary>
  105. public static readonly DependencyProperty CollapsedWidthProperty = DependencyProperty.Register(
  106. "CollapsedWidth", typeof(double), typeof(HamburgerMenu), new PropertyMetadata(default(double)));
  107. /// <summary>
  108. /// 折叠宽度
  109. /// </summary>
  110. public double CollapsedWidth
  111. {
  112. get { return (double)GetValue(CollapsedWidthProperty); }
  113. set { SetValue(CollapsedWidthProperty, value); }
  114. }
  115. /// <summary>
  116. /// 展开宽度
  117. /// </summary>
  118. public static readonly DependencyProperty ExpandedWidthProperty = DependencyProperty.Register(
  119. "ExpandedWidth", typeof(double), typeof(HamburgerMenu), new PropertyMetadata(default(double)));
  120. /// <summary>
  121. /// 展开宽度
  122. /// </summary>
  123. public double ExpandedWidth
  124. {
  125. get { return (double)GetValue(ExpandedWidthProperty); }
  126. set { SetValue(ExpandedWidthProperty, value); }
  127. }
  128. /// <summary>
  129. /// 是否显示汉堡包按钮
  130. /// </summary>
  131. public static readonly DependencyProperty IsShowHamburgerButtonProperty = DependencyProperty.Register(
  132. "IsShowHamburgerButton", typeof(bool), typeof(HamburgerMenu), new PropertyMetadata(BooleanBoxes.TrueBox));
  133. /// <summary>
  134. /// 是否显示汉堡包按钮
  135. /// </summary>
  136. public bool IsShowHamburgerButton
  137. {
  138. get { return (bool)GetValue(IsShowHamburgerButtonProperty); }
  139. set { SetValue(IsShowHamburgerButtonProperty, BooleanBoxes.Box(value)); }
  140. }
  141. /// <summary>
  142. /// 是否显示小竖条
  143. /// </summary>
  144. public static readonly DependencyProperty IsShowLittleBarProperty = DependencyProperty.Register(
  145. "IsShowLittleBar", typeof(bool), typeof(HamburgerMenu), new PropertyMetadata(BooleanBoxes.TrueBox));
  146. /// <summary>
  147. /// 是否显示小竖条
  148. /// </summary>
  149. public bool IsShowLittleBar
  150. {
  151. get { return (bool)GetValue(IsShowLittleBarProperty); }
  152. set { SetValue(IsShowLittleBarProperty, BooleanBoxes.Box(value)); }
  153. }
  154. /// <summary>
  155. /// 转换类型
  156. /// </summary>
  157. public static readonly DependencyProperty TransitionTypeProperty =
  158. DependencyProperty.Register("TransitionType", typeof(TransitionType), typeof(HamburgerMenu), new PropertyMetadata(default(TransitionType)));
  159. /// <summary>
  160. /// 转换类型
  161. /// </summary>
  162. public TransitionType TransitionType
  163. {
  164. get { return (TransitionType)GetValue(TransitionTypeProperty); }
  165. set { SetValue(TransitionTypeProperty, value); }
  166. }
  167. /// <summary>
  168. /// 动画时长
  169. /// </summary>
  170. public static readonly DependencyProperty TransitionDurationProperty =
  171. DependencyProperty.Register("TransitionDuration", typeof(Duration), typeof(HamburgerMenu), new PropertyMetadata(default(Duration)));
  172. /// <summary>
  173. /// 动画时长
  174. /// </summary>
  175. public Duration TransitionDuration
  176. {
  177. get { return (Duration)GetValue(TransitionDurationProperty); }
  178. set { SetValue(TransitionDurationProperty, value); }
  179. }
  180. /// <summary>
  181. /// 面板背景
  182. /// </summary>
  183. public static readonly DependencyProperty PaneBackgroundProperty =
  184. DependencyProperty.Register("PaneBackground", typeof(Brush), typeof(HamburgerMenu), new PropertyMetadata(default(Brush)));
  185. /// <summary>
  186. /// 面板背景
  187. /// </summary>
  188. public Brush PaneBackground
  189. {
  190. get { return (Brush)GetValue(PaneBackgroundProperty); }
  191. set { SetValue(PaneBackgroundProperty, value); }
  192. }
  193. /// <summary>
  194. /// 面板边框
  195. /// </summary>
  196. public static readonly DependencyProperty PaneBorderThicknessProperty =
  197. DependencyProperty.Register("PaneBorderThickness", typeof(Thickness), typeof(HamburgerMenu), new PropertyMetadata(default(Thickness)));
  198. /// <summary>
  199. /// 面板边框
  200. /// </summary>
  201. public Thickness PaneBorderThickness
  202. {
  203. get { return (Thickness)GetValue(PaneBorderThicknessProperty); }
  204. set { SetValue(PaneBorderThicknessProperty, value); }
  205. }
  206. /// <summary>
  207. /// 面板圆角半径
  208. /// </summary>
  209. public static readonly DependencyProperty PaneBorderCornerRadiusProperty =
  210. DependencyProperty.Register("PaneBorderCornerRadius", typeof(CornerRadius), typeof(HamburgerMenu), new PropertyMetadata(default(CornerRadius)));
  211. /// <summary>
  212. /// 面板圆角半径
  213. /// </summary>
  214. public CornerRadius PaneBorderCornerRadius
  215. {
  216. get { return (CornerRadius)GetValue(PaneBorderCornerRadiusProperty); }
  217. set { SetValue(PaneBorderCornerRadiusProperty, value); }
  218. }
  219. #endregion properties
  220. #region options item
  221. // form https://github.com/MahApps/MahApps.Metro
  222. // MahApps.Metro.Controls.HamburgerMenu
  223. /// <summary>Identifies the <see cref="OptionsItemsSource"/> dependency property.</summary>
  224. public static readonly DependencyProperty OptionsItemsSourceProperty = DependencyProperty.Register(
  225. nameof(OptionsItemsSource), typeof(IEnumerable), typeof(HamburgerMenu), new PropertyMetadata(null));
  226. /// <summary>
  227. /// Gets or sets an object source used to generate the content of the options.
  228. /// </summary>
  229. public IEnumerable OptionsItemsSource
  230. {
  231. get => (IEnumerable)this.GetValue(OptionsItemsSourceProperty);
  232. set => this.SetValue(OptionsItemsSourceProperty, value);
  233. }
  234. /// <summary>Identifies the <see cref="OptionsItemContainerStyle"/> dependency property.</summary>
  235. public static readonly DependencyProperty OptionsItemContainerStyleProperty = DependencyProperty.Register(
  236. nameof(OptionsItemContainerStyle), typeof(Style), typeof(HamburgerMenu), new PropertyMetadata(null));
  237. /// <summary>
  238. /// Gets or sets the <see cref="Style"/> used for each item in the options.
  239. /// </summary>
  240. public Style OptionsItemContainerStyle
  241. {
  242. get => (Style)this.GetValue(OptionsItemContainerStyleProperty);
  243. set => this.SetValue(OptionsItemContainerStyleProperty, value);
  244. }
  245. /// <summary>Identifies the <see cref="OptionsItemTemplate"/> dependency property.</summary>
  246. public static readonly DependencyProperty OptionsItemTemplateProperty = DependencyProperty.Register(
  247. nameof(OptionsItemTemplate), typeof(DataTemplate), typeof(HamburgerMenu), new PropertyMetadata(null));
  248. /// <summary>
  249. /// Gets or sets the <see cref="DataTemplate"/> used to display each item in the options.
  250. /// </summary>
  251. public DataTemplate OptionsItemTemplate
  252. {
  253. get => (DataTemplate)this.GetValue(OptionsItemTemplateProperty);
  254. set => this.SetValue(OptionsItemTemplateProperty, value);
  255. }
  256. /// <summary>Identifies the <see cref="OptionsItemTemplateSelector"/> dependency property.</summary>
  257. public static readonly DependencyProperty OptionsItemTemplateSelectorProperty = DependencyProperty.Register(
  258. nameof(OptionsItemTemplateSelector), typeof(DataTemplateSelector), typeof(HamburgerMenu), new PropertyMetadata(null));
  259. /// <summary>
  260. /// Gets or sets the <see cref="DataTemplateSelector"/> used to display each item in the options.
  261. /// </summary>
  262. public DataTemplateSelector OptionsItemTemplateSelector
  263. {
  264. get => (DataTemplateSelector)this.GetValue(OptionsItemTemplateSelectorProperty);
  265. set => this.SetValue(OptionsItemTemplateSelectorProperty, value);
  266. }
  267. /// <summary>Identifies the <see cref="OptionsVisibility"/> dependency property.</summary>
  268. public static readonly DependencyProperty OptionsVisibilityProperty = DependencyProperty.Register(
  269. nameof(OptionsVisibility), typeof(Visibility), typeof(HamburgerMenu), new PropertyMetadata(Visibility.Visible));
  270. /// <summary>
  271. /// Gets or sets the <see cref="Visibility"/> of the options menu.
  272. /// </summary>
  273. public Visibility OptionsVisibility
  274. {
  275. get => (Visibility)this.GetValue(OptionsVisibilityProperty);
  276. set => this.SetValue(OptionsVisibilityProperty, value);
  277. }
  278. /// <summary>
  279. /// Gets the collection used to generate the content of the option list.
  280. /// </summary>
  281. /// <exception cref="Exception">
  282. /// Exception thrown if OptionsListView is not yet defined.
  283. /// </exception>
  284. public ItemCollection OptionsItems
  285. {
  286. get
  287. {
  288. if (this.optionsMenu is null)
  289. {
  290. throw new Exception("OptionsListView is not defined yet. Please use OptionsItemsSource instead.");
  291. }
  292. return this.optionsMenu.Items;
  293. }
  294. }
  295. #endregion options item
  296. #region events
  297. /// <summary>
  298. /// 汉堡包按钮点击事件
  299. /// </summary>
  300. public static readonly RoutedEvent HamburgerButtonClickEvent = EventManager.RegisterRoutedEvent(nameof(HamburgerButtonClick), RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(HamburgerMenu));
  301. /// <summary>
  302. /// 汉堡包按钮点击
  303. /// </summary>
  304. public event RoutedEventHandler HamburgerButtonClick
  305. {
  306. add => this.AddHandler(HamburgerButtonClickEvent, value);
  307. remove => this.RemoveHandler(HamburgerButtonClickEvent, value);
  308. }
  309. #endregion events
  310. static HamburgerMenu()
  311. {
  312. DefaultStyleKeyProperty.OverrideMetadata(typeof(HamburgerMenu), new FrameworkPropertyMetadata(typeof(HamburgerMenu)));
  313. }
  314. #region methods
  315. /// <inheritdoc/>
  316. public override void OnApplyTemplate()
  317. {
  318. base.OnApplyTemplate();
  319. Button button = GetTemplateChild(HamburgerButtonPartName) as Button;
  320. button.Click += Button_Click;
  321. optionsMenu = GetTemplateChild(OptionsItemsControlPartName) as MenuBase;
  322. contentTransition = GetTemplateChild(ContentTransitionPartName) as Transition;
  323. SelectionChanged += HamburgerMenu_SelectionChanged;
  324. }
  325. private void HamburgerMenu_SelectionChanged(object sender, SelectionChangedEventArgs e)
  326. {
  327. if (e.OriginalSource.GetHashCode() == GetHashCode())
  328. {
  329. Transition.ShowAnimation(contentTransition);
  330. }
  331. }
  332. /// <inheritdoc/>
  333. protected override bool IsItemItsOwnContainerOverride(object item)
  334. {
  335. return item is HamburgerMenuItem;
  336. }
  337. /// <inheritdoc/>
  338. protected override DependencyObject GetContainerForItemOverride()
  339. {
  340. return new HamburgerMenuItem();
  341. }
  342. private void Button_Click(object sender, RoutedEventArgs e)
  343. {
  344. var args = new RoutedEventArgs(HamburgerButtonClickEvent, sender);
  345. this.RaiseEvent(args);
  346. if (!args.Handled)
  347. {
  348. IsExpanded = !IsExpanded;
  349. }
  350. }
  351. #endregion methods
  352. }
  353. }