diff --git a/NeoUI/NeoUI/Assists/PopupAssist.cs b/NeoUI/NeoUI/Assists/PopupAssist.cs new file mode 100644 index 0000000..687c09d --- /dev/null +++ b/NeoUI/NeoUI/Assists/PopupAssist.cs @@ -0,0 +1,140 @@ +// NeoUI/Assists/PopupAssist.cs +using System; +using System.Windows; +using System.Windows.Controls.Primitives; +using System.Windows.Input; + +namespace NeoUI.Assists +{ + /// + /// 提供附加属性,为 StaysOpen="True" 的 Popup 附加原生 ComboBox 的标准行为。 + /// 当 SimulateNativeBehavior="True" 时: + /// 1. 点击外部、拖动窗口或窗口失活时,Popup 会自动关闭。 + /// 2. Popup 打开时,会阻止外部内容的鼠标滚轮滚动。 + /// + public static class PopupAssist + { + #region SimulateNativeBehavior Attached Property + + public static readonly DependencyProperty SimulateNativeBehaviorProperty = + DependencyProperty.RegisterAttached( + "SimulateNativeBehavior", // 【命名已更新】 + typeof(bool), + typeof(PopupAssist), + new PropertyMetadata(false, OnSimulateNativeBehaviorChanged)); // 【回调已更新】 + + public static void SetSimulateNativeBehavior(DependencyObject element, bool value) + { + element.SetValue(SimulateNativeBehaviorProperty, value); + } + + public static bool GetSimulateNativeBehavior(DependencyObject element) + { + return (bool)element.GetValue(SimulateNativeBehaviorProperty); + } + + #endregion + + #region Private State Management Properties + + private static readonly DependencyProperty PreviewMouseDownHandlerProperty = + DependencyProperty.RegisterAttached("PreviewMouseDownHandler", typeof(MouseButtonEventHandler), typeof(PopupAssist)); + + private static readonly DependencyProperty WindowEventHandlerProperty = + DependencyProperty.RegisterAttached("WindowEventHandler", typeof(EventHandler), typeof(PopupAssist)); + + private static readonly DependencyProperty PreviewMouseWheelHandlerProperty = + DependencyProperty.RegisterAttached("PreviewMouseWheelHandler", typeof(MouseWheelEventHandler), typeof(PopupAssist)); + + #endregion + + // 【回调已更新】 + private static void OnSimulateNativeBehaviorChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + if (!(d is Popup popup)) return; + + if ((bool)e.NewValue) + { + popup.Opened += OnPopupOpened; + } + else + { + popup.Opened -= OnPopupOpened; + } + } + + private static void OnPopupOpened(object sender, EventArgs e) + { + var popup = sender as Popup; + if (popup == null) return; + + var window = Window.GetWindow(popup); + if (window == null) return; + + // --- 自动关闭逻辑 --- + EventHandler windowEventHandler = (s, args) => + { + popup.Dispatcher.BeginInvoke(new Action(() => { + popup.IsOpen = false; + })); + }; + popup.SetValue(WindowEventHandlerProperty, windowEventHandler); + window.Deactivated += windowEventHandler; + window.LocationChanged += windowEventHandler; + + MouseButtonEventHandler mouseDownHandler = (s, args) => + { + if (popup.IsMouseOver || (popup.PlacementTarget != null && popup.PlacementTarget.IsMouseOver)) + { + return; + } + popup.IsOpen = false; + }; + popup.SetValue(PreviewMouseDownHandlerProperty, mouseDownHandler); + window.AddHandler(UIElement.PreviewMouseDownEvent, mouseDownHandler, true); + + // --- 鼠标滚轮拦截逻辑 --- + MouseWheelEventHandler mouseWheelHandler = (s, args) => + { + if (!popup.IsMouseOver) + { + args.Handled = true; + } + }; + popup.SetValue(PreviewMouseWheelHandlerProperty, mouseWheelHandler); + window.AddHandler(UIElement.PreviewMouseWheelEvent, mouseWheelHandler, true); + + popup.Closed += OnPopupClosed; + } + + private static void OnPopupClosed(object sender, EventArgs e) + { + var popup = sender as Popup; + if (popup == null) return; + + popup.Closed -= OnPopupClosed; + + var window = Window.GetWindow(popup); + if (window == null) return; + + if (popup.GetValue(WindowEventHandlerProperty) is EventHandler windowEventHandler) + { + window.Deactivated -= windowEventHandler; + window.LocationChanged -= windowEventHandler; + popup.ClearValue(WindowEventHandlerProperty); + } + + if (popup.GetValue(PreviewMouseDownHandlerProperty) is MouseButtonEventHandler mouseDownHandler) + { + window.RemoveHandler(UIElement.PreviewMouseDownEvent, mouseDownHandler); + popup.ClearValue(PreviewMouseDownHandlerProperty); + } + + if (popup.GetValue(PreviewMouseWheelHandlerProperty) is MouseWheelEventHandler mouseWheelHandler) + { + window.RemoveHandler(UIElement.PreviewMouseWheelEvent, mouseWheelHandler); + popup.ClearValue(PreviewMouseWheelHandlerProperty); + } + } + } +} \ No newline at end of file diff --git a/NeoUI/NeoUI/Controls/Accordion.xaml b/NeoUI/NeoUI/Controls/Accordion.xaml index e3d11e4..8efe4be 100644 --- a/NeoUI/NeoUI/Controls/Accordion.xaml +++ b/NeoUI/NeoUI/Controls/Accordion.xaml @@ -1,16 +1,16 @@  - - --> + - + + @@ -142,7 +201,7 @@ MinWidth="{Binding ActualWidth, ElementName=templateRoot}" MaxHeight="{TemplateBinding MaxDropDownHeight}" Margin="8" - Background="{DynamicResource BackgroundLayoutBrush}" + Background="{DynamicResource BackgroundFloatingBrush}" BorderBrush="{DynamicResource BorderNormalBrush}" BorderThickness="1" CornerRadius="4"> @@ -179,7 +238,7 @@ BorderThickness="{TemplateBinding BorderThickness}" IsChecked="{Binding IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}" Style="{StaticResource ComboBoxToggleButton}" /> - + + @@ -371,68 +431,10 @@ + --> - - - - - + + + + + + diff --git a/NeoUI/NeoUI/Controls/TextBoxStyle.xaml b/NeoUI/NeoUI/Controls/TextBoxStyle.xaml index 4283777..9dc2e54 100644 --- a/NeoUI/NeoUI/Controls/TextBoxStyle.xaml +++ b/NeoUI/NeoUI/Controls/TextBoxStyle.xaml @@ -3,7 +3,7 @@ xmlns:assists="clr-namespace:NeoUI.Assists" xmlns:controls="clr-namespace:NeoUI.Controls" xmlns:converters="clr-namespace:NeoUI.Converters" - xmlns:svd="clr-namespace:NeoUI.Controls.Decorations" + xmlns:decorations="clr-namespace:NeoUI.Controls.Decorations" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> @@ -77,7 +77,7 @@ - - - - + @@ -462,7 +462,7 @@ To="0.2" /> - - + diff --git a/NeoUI/NeoUI/Controls/Toast/ToastControl.xaml b/NeoUI/NeoUI/Controls/Toast/ToastControl.xaml index cb04453..7133fcb 100644 --- a/NeoUI/NeoUI/Controls/Toast/ToastControl.xaml +++ b/NeoUI/NeoUI/Controls/Toast/ToastControl.xaml @@ -3,7 +3,6 @@ xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" - xmlns:local="clr-namespace:NeoUI.Controls" xmlns:controls="clr-namespace:NeoUI.Controls" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" Width="320" diff --git a/NeoUI/NeoUI/Controls/ToggleButtonStyle.xaml b/NeoUI/NeoUI/Controls/ToggleButtonStyle.xaml index 01a5cad..4a5663d 100644 --- a/NeoUI/NeoUI/Controls/ToggleButtonStyle.xaml +++ b/NeoUI/NeoUI/Controls/ToggleButtonStyle.xaml @@ -3,7 +3,7 @@ xmlns:assist="clr-namespace:NeoUI.Assists" xmlns:converters="clr-namespace:NeoUI.Converters" xmlns:internal="clr-namespace:NeoUI.Converters.Internal" - xmlns:svd="clr-namespace:NeoUI.Controls.Decorations" + xmlns:decorations="clr-namespace:NeoUI.Controls.Decorations" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> -