diff --git a/src/Shared/HandyControl_Shared/Controls/Attach/MenuTopLineAttach.cs b/src/Shared/HandyControl_Shared/Controls/Attach/MenuTopLineAttach.cs new file mode 100644 index 00000000..81be40a2 --- /dev/null +++ b/src/Shared/HandyControl_Shared/Controls/Attach/MenuTopLineAttach.cs @@ -0,0 +1,99 @@ +using System; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Controls.Primitives; +using HandyControl.Tools; +using HandyControl.Tools.Interop; + +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 popup) + { + if (popup.TemplatedParent is MenuItem menuItem) + { + SetTopLine(menuItem, topLine); + menuItem.Unloaded += MenuItem_Unloaded; + } + 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(InteropMethods.GetCursorPos(), 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); + } +} diff --git a/src/Shared/HandyControl_Shared/HandyControl_Shared.projitems b/src/Shared/HandyControl_Shared/HandyControl_Shared.projitems index 1ec85042..cf7a8042 100644 --- a/src/Shared/HandyControl_Shared/HandyControl_Shared.projitems +++ b/src/Shared/HandyControl_Shared/HandyControl_Shared.projitems @@ -17,6 +17,7 @@ + diff --git a/src/Shared/HandyControl_Shared/Themes/Styles/Base/MenuBaseStyle.xaml b/src/Shared/HandyControl_Shared/Themes/Styles/Base/MenuBaseStyle.xaml index bd7b0652..4bc5ed76 100644 --- a/src/Shared/HandyControl_Shared/Themes/Styles/Base/MenuBaseStyle.xaml +++ b/src/Shared/HandyControl_Shared/Themes/Styles/Base/MenuBaseStyle.xaml @@ -3,6 +3,7 @@ xmlns:controls="clr-namespace:HandyControl.Controls"> + @@ -64,13 +65,13 @@ - + - + diff --git a/src/Shared/HandyControl_Shared/Tools/Converter/TreeViewItemMarginConverter.cs b/src/Shared/HandyControl_Shared/Tools/Converter/TreeViewItemMarginConverter.cs index b8872602..a6bbd3e7 100644 --- a/src/Shared/HandyControl_Shared/Tools/Converter/TreeViewItemMarginConverter.cs +++ b/src/Shared/HandyControl_Shared/Tools/Converter/TreeViewItemMarginConverter.cs @@ -8,7 +8,6 @@ namespace HandyControl.Tools.Converter { public class TreeViewItemMarginConverter: IValueConverter { - public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { var left = 0.0; diff --git a/src/Shared/HandyControl_Shared/Tools/Helper/VisualHelper.cs b/src/Shared/HandyControl_Shared/Tools/Helper/VisualHelper.cs index b6351478..84a088e1 100644 --- a/src/Shared/HandyControl_Shared/Tools/Helper/VisualHelper.cs +++ b/src/Shared/HandyControl_Shared/Tools/Helper/VisualHelper.cs @@ -20,12 +20,10 @@ namespace HandyControl.Tools .FirstOrDefault(group => string.CompareOrdinal(groupName, group.Name) == 0); } - internal static FrameworkElement GetImplementationRoot(DependencyObject d) - { - return 1 == VisualTreeHelper.GetChildrenCount(d) + internal static FrameworkElement GetImplementationRoot(DependencyObject d) => + 1 == VisualTreeHelper.GetChildrenCount(d) ? VisualTreeHelper.GetChild(d, 0) as FrameworkElement : null; - } public static T GetChild(DependencyObject d) where T : DependencyObject { @@ -42,16 +40,19 @@ namespace HandyControl.Tools return default; } - public static IntPtr GetHandle(this Visual visual) - { - return (PresentationSource.FromVisual(visual) as HwndSource)?.Handle ?? IntPtr.Zero; - } + public static T GetParent(DependencyObject d) where T : DependencyObject => + d switch + { + null => default, + T t => t, + Window _ => null, + _ => GetParent(VisualTreeHelper.GetParent(d)) + }; - internal static void HitTestVisibleElements(Visual visual, HitTestResultCallback resultCallback, - HitTestParameters parameters) - { + public static IntPtr GetHandle(this Visual visual) => (PresentationSource.FromVisual(visual) as HwndSource)?.Handle ?? IntPtr.Zero; + + internal static void HitTestVisibleElements(Visual visual, HitTestResultCallback resultCallback, HitTestParameters parameters) => VisualTreeHelper.HitTest(visual, ExcludeNonVisualElements, resultCallback, parameters); - } private static HitTestFilterBehavior ExcludeNonVisualElements(DependencyObject potentialHitTestTarget) {