ContextMenu.cs 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. namespace Avalonia.Controls
  2. {
  3. using Input;
  4. using Interactivity;
  5. using LogicalTree;
  6. using Primitives;
  7. using System;
  8. using System.Reactive.Linq;
  9. using System.Linq;
  10. public class ContextMenu : SelectingItemsControl
  11. {
  12. private bool _isOpen;
  13. private Popup _popup;
  14. /// <summary>
  15. /// Initializes static members of the <see cref="ContextMenu"/> class.
  16. /// </summary>
  17. static ContextMenu()
  18. {
  19. ContextMenuProperty.Changed.Subscribe(ContextMenuChanged);
  20. MenuItem.ClickEvent.AddClassHandler<ContextMenu>(x => x.OnContextMenuClick, handledEventsToo: true);
  21. }
  22. /// <summary>
  23. /// Called when the <see cref="Control.ContextMenu"/> property changes on a control.
  24. /// </summary>
  25. /// <param name="e">The event args.</param>
  26. private static void ContextMenuChanged(AvaloniaPropertyChangedEventArgs e)
  27. {
  28. var control = (Control)e.Sender;
  29. if (e.OldValue != null)
  30. {
  31. control.PointerReleased -= ControlPointerReleased;
  32. }
  33. if (e.NewValue != null)
  34. {
  35. control.PointerReleased += ControlPointerReleased;
  36. }
  37. }
  38. /// <summary>
  39. /// Called when a submenu is clicked somewhere in the menu.
  40. /// </summary>
  41. /// <param name="e">The event args.</param>
  42. private void OnContextMenuClick(RoutedEventArgs e)
  43. {
  44. Hide();
  45. FocusManager.Instance.Focus(null);
  46. e.Handled = true;
  47. }
  48. /// <summary>
  49. /// Closes the menu.
  50. /// </summary>
  51. public void Hide()
  52. {
  53. if (_popup != null && _popup.IsVisible)
  54. {
  55. _popup.Close();
  56. }
  57. SelectedIndex = -1;
  58. _isOpen = false;
  59. }
  60. /// <summary>
  61. /// Shows a context menu for the specified control.
  62. /// </summary>
  63. /// <param name="control">The control.</param>
  64. private void Show(Control control)
  65. {
  66. if (control != null)
  67. {
  68. if (_popup == null)
  69. {
  70. _popup = new Popup()
  71. {
  72. PlacementMode = PlacementMode.Pointer,
  73. PlacementTarget = control,
  74. StaysOpen = false,
  75. ObeyScreenEdges = true
  76. };
  77. _popup.Closed += PopupClosed;
  78. }
  79. ((ISetLogicalParent)_popup).SetParent(control);
  80. _popup.Child = control.ContextMenu;
  81. _popup.Open();
  82. control.ContextMenu._isOpen = true;
  83. }
  84. }
  85. private static void PopupClosed(object sender, EventArgs e)
  86. {
  87. var contextMenu = (sender as Popup)?.Child as ContextMenu;
  88. if (contextMenu != null)
  89. {
  90. foreach (var i in contextMenu.GetLogicalChildren().OfType<MenuItem>())
  91. {
  92. i.IsSubMenuOpen = false;
  93. }
  94. contextMenu._isOpen = false;
  95. contextMenu.SelectedIndex = -1;
  96. }
  97. }
  98. private static void ControlPointerReleased(object sender, PointerReleasedEventArgs e)
  99. {
  100. var control = (Control)sender;
  101. var contextMenu = control.ContextMenu;
  102. if (e.MouseButton == MouseButton.Right)
  103. {
  104. if (control.ContextMenu._isOpen)
  105. {
  106. control.ContextMenu.Hide();
  107. }
  108. contextMenu.Show(control);
  109. e.Handled = true;
  110. }
  111. else if (contextMenu._isOpen)
  112. {
  113. control.ContextMenu.Hide();
  114. e.Handled = true;
  115. }
  116. }
  117. }
  118. }