using System; using System.Windows; using System.Windows.Controls; using System.Windows.Controls.Primitives; using HandyControl.Tools; namespace HandyControl.Controls; public class MenuTopLineAttach { public static readonly DependencyProperty PopupProperty = DependencyProperty.RegisterAttached( "Popup", typeof(Popup), typeof(MenuTopLineAttach), new PropertyMetadata(default(Popup), OnPopupChanged)); private static void OnPopupChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var topLine = (FrameworkElement) d; if (e.NewValue is Popup { TemplatedParent: MenuItem menuItem }) { SetTopLine(menuItem, topLine); menuItem.Loaded += MenuItem_Loaded; } } private static void MenuItem_Loaded(object sender, RoutedEventArgs e) { var menuItem = (FrameworkElement) sender; menuItem.Unloaded += MenuItem_Unloaded; var topLine = GetTopLine(menuItem); var popup = GetPopup(topLine); if (popup != null) { popup.Opened += Popup_Opened; } } private static void MenuItem_Unloaded(object sender, RoutedEventArgs e) { var menuItem = (FrameworkElement) sender; menuItem.Unloaded -= MenuItem_Unloaded; var topLine = GetTopLine(menuItem); var popup = GetPopup(topLine); if (popup != null) { popup.Opened -= Popup_Opened; } } private static void Popup_Opened(object sender, EventArgs e) { var popup = (Popup) sender; if (popup.TemplatedParent is MenuItem menuItem) { var topLine = GetTopLine(menuItem); if (topLine == null) return; topLine.HorizontalAlignment = HorizontalAlignment.Left; topLine.Width = menuItem.ActualWidth; topLine.Margin = new Thickness(); var positionLeftTop = menuItem.PointToScreen(new Point()); var positionRightBottom = menuItem.PointToScreen(new Point(menuItem.ActualWidth, menuItem.ActualHeight)); ScreenHelper.FindMonitorRectsFromPoint(positionLeftTop, out _, out var workAreaRect); var panel = VisualHelper.GetParent(topLine); if (positionLeftTop.X < 0) { topLine.Margin = new Thickness(positionLeftTop.X - panel.Margin.Left, 0, 0, 0); } else if (positionLeftTop.X + panel.ActualWidth > workAreaRect.Right) { var overflowWidth = positionRightBottom.X - workAreaRect.Right; if (overflowWidth > 0) { topLine.Width -= overflowWidth + panel.Margin.Right; } topLine.HorizontalAlignment = HorizontalAlignment.Left; topLine.Margin = new Thickness(positionLeftTop.X + panel.ActualWidth - workAreaRect.Right + panel.Margin.Right, 0, 0, 0); } if (positionRightBottom.Y > workAreaRect.Bottom) { topLine.Width = 0; topLine.HorizontalAlignment = HorizontalAlignment.Stretch; topLine.Margin = new Thickness(); } } } public static void SetPopup(DependencyObject element, Popup value) => element.SetValue(PopupProperty, value); public static Popup GetPopup(DependencyObject element) => (Popup) element.GetValue(PopupProperty); internal static readonly DependencyProperty TopLineProperty = DependencyProperty.RegisterAttached( "TopLine", typeof(FrameworkElement), typeof(MenuTopLineAttach), new PropertyMetadata(default(FrameworkElement))); internal static void SetTopLine(DependencyObject element, FrameworkElement value) => element.SetValue(TopLineProperty, value); internal static FrameworkElement GetTopLine(DependencyObject element) => (FrameworkElement) element.GetValue(TopLineProperty); }