MenuTopLineAttach.cs 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104
  1. using System;
  2. using System.Windows;
  3. using System.Windows.Controls;
  4. using System.Windows.Controls.Primitives;
  5. using HandyControl.Tools;
  6. namespace HandyControl.Controls;
  7. public class MenuTopLineAttach
  8. {
  9. public static readonly DependencyProperty PopupProperty = DependencyProperty.RegisterAttached(
  10. "Popup", typeof(Popup), typeof(MenuTopLineAttach), new PropertyMetadata(default(Popup), OnPopupChanged));
  11. private static void OnPopupChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
  12. {
  13. var topLine = (FrameworkElement) d;
  14. if (e.NewValue is Popup { TemplatedParent: MenuItem menuItem })
  15. {
  16. SetTopLine(menuItem, topLine);
  17. menuItem.Loaded += MenuItem_Loaded;
  18. }
  19. }
  20. private static void MenuItem_Loaded(object sender, RoutedEventArgs e)
  21. {
  22. var menuItem = (FrameworkElement) sender;
  23. menuItem.Unloaded += MenuItem_Unloaded;
  24. var topLine = GetTopLine(menuItem);
  25. var popup = GetPopup(topLine);
  26. if (popup != null)
  27. {
  28. popup.Opened += Popup_Opened;
  29. }
  30. }
  31. private static void MenuItem_Unloaded(object sender, RoutedEventArgs e)
  32. {
  33. var menuItem = (FrameworkElement) sender;
  34. menuItem.Unloaded -= MenuItem_Unloaded;
  35. var topLine = GetTopLine(menuItem);
  36. var popup = GetPopup(topLine);
  37. if (popup != null)
  38. {
  39. popup.Opened -= Popup_Opened;
  40. }
  41. }
  42. private static void Popup_Opened(object sender, EventArgs e)
  43. {
  44. var popup = (Popup) sender;
  45. if (popup.TemplatedParent is MenuItem menuItem)
  46. {
  47. var topLine = GetTopLine(menuItem);
  48. if (topLine == null) return;
  49. topLine.HorizontalAlignment = HorizontalAlignment.Left;
  50. topLine.Width = menuItem.ActualWidth;
  51. topLine.Margin = new Thickness();
  52. var positionLeftTop = menuItem.PointToScreen(new Point());
  53. var positionRightBottom = menuItem.PointToScreen(new Point(menuItem.ActualWidth, menuItem.ActualHeight));
  54. ScreenHelper.FindMonitorRectsFromPoint(positionLeftTop, out _, out var workAreaRect);
  55. var panel = VisualHelper.GetParent<Panel>(topLine);
  56. if (positionLeftTop.X < 0)
  57. {
  58. topLine.Margin = new Thickness(positionLeftTop.X - panel.Margin.Left, 0, 0, 0);
  59. }
  60. else if (positionLeftTop.X + panel.ActualWidth > workAreaRect.Right)
  61. {
  62. var overflowWidth = positionRightBottom.X - workAreaRect.Right;
  63. if (overflowWidth > 0)
  64. {
  65. topLine.Width -= overflowWidth + panel.Margin.Right;
  66. }
  67. topLine.HorizontalAlignment = HorizontalAlignment.Left;
  68. topLine.Margin = new Thickness(positionLeftTop.X + panel.ActualWidth - workAreaRect.Right + panel.Margin.Right, 0, 0, 0);
  69. }
  70. if (positionRightBottom.Y > workAreaRect.Bottom)
  71. {
  72. topLine.Width = 0;
  73. topLine.HorizontalAlignment = HorizontalAlignment.Stretch;
  74. topLine.Margin = new Thickness();
  75. }
  76. }
  77. }
  78. public static void SetPopup(DependencyObject element, Popup value)
  79. => element.SetValue(PopupProperty, value);
  80. public static Popup GetPopup(DependencyObject element)
  81. => (Popup) element.GetValue(PopupProperty);
  82. internal static readonly DependencyProperty TopLineProperty = DependencyProperty.RegisterAttached(
  83. "TopLine", typeof(FrameworkElement), typeof(MenuTopLineAttach), new PropertyMetadata(default(FrameworkElement)));
  84. internal static void SetTopLine(DependencyObject element, FrameworkElement value)
  85. => element.SetValue(TopLineProperty, value);
  86. internal static FrameworkElement GetTopLine(DependencyObject element)
  87. => (FrameworkElement) element.GetValue(TopLineProperty);
  88. }