From 4aaca366f05c5fc04484afe948dc70332a6817e1 Mon Sep 17 00:00:00 2001 From: DarkFlameMaster <1004452714@qq.com> Date: Tue, 24 Feb 2026 15:21:20 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20JS=E7=BA=A7=E8=81=94=E9=80=89=E6=8B=A9?= =?UTF-8?q?=20=E5=92=8C=20=E7=A7=98=E5=A2=83=E9=80=89=E6=8B=A9=20=E6=BB=9A?= =?UTF-8?q?=E8=BD=AE=E4=BA=8B=E4=BB=B6=E7=A6=81=E6=AD=A2=E7=A9=BF=E9=80=8F?= =?UTF-8?q?=E8=87=B3=E5=85=B6=E4=BB=96=E7=AA=97=E5=8F=A3=20(#2828)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../View/Controls/CascadeSelector.xaml | 13 +- .../View/Controls/CascadeSelector.xaml.cs | 119 +++++++++++++++++ .../View/Controls/DomainSelector.xaml | 24 ++-- .../View/Controls/DomainSelector.xaml.cs | 120 ++++++++++++++++++ 4 files changed, 264 insertions(+), 12 deletions(-) diff --git a/BetterGenshinImpact/View/Controls/CascadeSelector.xaml b/BetterGenshinImpact/View/Controls/CascadeSelector.xaml index c0b874bb..cd41177c 100644 --- a/BetterGenshinImpact/View/Controls/CascadeSelector.xaml +++ b/BetterGenshinImpact/View/Controls/CascadeSelector.xaml @@ -44,7 +44,9 @@ PlacementTarget="{Binding ElementName=MainToggle}" StaysOpen="False" AllowsTransparency="True" - PopupAnimation="Slide"> + PopupAnimation="Slide" + Opened="MainPopup_Opened" + Closed="MainPopup_Closed"> + MaxWidth="600" + PreviewMouseWheel="PopupBorder_PreviewMouseWheel"> @@ -70,7 +73,8 @@ ItemsSource="{Binding FirstLevelOptions, ElementName=Root}" BorderThickness="0" SelectionChanged="FirstLevelListView_SelectionChanged" - ScrollViewer.VerticalScrollBarVisibility="Auto"> + ScrollViewer.VerticalScrollBarVisibility="Auto" + ScrollViewer.CanContentScroll="False"> @@ -86,7 +90,8 @@ ItemsSource="{Binding SecondLevelOptions, ElementName=Root}" BorderThickness="0" SelectionChanged="SecondLevelListView_SelectionChanged" - ScrollViewer.VerticalScrollBarVisibility="Auto"> + ScrollViewer.VerticalScrollBarVisibility="Auto" + ScrollViewer.CanContentScroll="False"> diff --git a/BetterGenshinImpact/View/Controls/CascadeSelector.xaml.cs b/BetterGenshinImpact/View/Controls/CascadeSelector.xaml.cs index de66604e..343949f1 100644 --- a/BetterGenshinImpact/View/Controls/CascadeSelector.xaml.cs +++ b/BetterGenshinImpact/View/Controls/CascadeSelector.xaml.cs @@ -1,8 +1,10 @@ +using System; using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Windows; using System.Windows.Controls; +using System.Windows.Input; using System.Windows.Media; namespace BetterGenshinImpact.View.Controls; @@ -21,6 +23,7 @@ public partial class CascadeSelector : UserControl { InitializeComponent(); Loaded += OnLoaded; + Unloaded += OnUnloaded; } private void OnLoaded(object sender, RoutedEventArgs e) @@ -29,6 +32,14 @@ public partial class CascadeSelector : UserControl UpdatePopupWidth(); } + /// + /// 控件卸载时清理事件处理器,防止内存泄漏 + /// + private void OnUnloaded(object sender, RoutedEventArgs e) + { + RemoveWindowMouseWheelHandler(); + } + public Dictionary>? CascadeOptions { get { return (Dictionary>?)GetValue(CascadeOptionsProperty); } @@ -277,4 +288,112 @@ public partial class CascadeSelector : UserControl } } } + + /// + /// Popup 打开时添加全局滚轮事件拦截 + /// + private void MainPopup_Opened(object sender, EventArgs e) + { + var window = Window.GetWindow(this); + if (window != null) + { + window.PreviewMouseWheel -= Window_PreviewMouseWheel; + window.PreviewMouseWheel += Window_PreviewMouseWheel; + } + } + + /// + /// Popup 关闭时移除全局滚轮事件拦截 + /// + private void MainPopup_Closed(object sender, EventArgs e) + { + RemoveWindowMouseWheelHandler(); + } + + /// + /// 移除窗口级滚轮事件处理器 + /// + private void RemoveWindowMouseWheelHandler() + { + var window = Window.GetWindow(this); + if (window != null) + { + window.PreviewMouseWheel -= Window_PreviewMouseWheel; + } + } + + /// + /// 全局滚轮事件处理,当 Popup 打开时拦截所有滚轮事件 + /// + private void Window_PreviewMouseWheel(object sender, MouseWheelEventArgs e) + { + if (MainPopup.IsOpen) + { + e.Handled = true; + + var scrollViewer1 = FindScrollViewer(FirstLevelListView); + var scrollViewer2 = FindScrollViewer(SecondLevelListView); + + if (scrollViewer1 != null && scrollViewer1.IsMouseOver) + { + scrollViewer1.ScrollToVerticalOffset(scrollViewer1.VerticalOffset - e.Delta / 2.0); + return; + } + + if (scrollViewer2 != null && scrollViewer2.IsMouseOver) + { + scrollViewer2.ScrollToVerticalOffset(scrollViewer2.VerticalOffset - e.Delta / 2.0); + return; + } + } + } + + /// + /// 处理 Popup 内的鼠标滚轮事件,防止滚动穿透到外部页面 + /// + private void PopupBorder_PreviewMouseWheel(object sender, MouseWheelEventArgs e) + { + e.Handled = true; + + var scrollViewer1 = FindScrollViewer(FirstLevelListView); + var scrollViewer2 = FindScrollViewer(SecondLevelListView); + + if (scrollViewer1 != null && scrollViewer1.IsMouseOver) + { + scrollViewer1.ScrollToVerticalOffset(scrollViewer1.VerticalOffset - e.Delta / 2.0); + return; + } + + if (scrollViewer2 != null && scrollViewer2.IsMouseOver) + { + scrollViewer2.ScrollToVerticalOffset(scrollViewer2.VerticalOffset - e.Delta / 2.0); + return; + } + } + + /// + /// 在视觉树中查找 ScrollViewer + /// + private ScrollViewer? FindScrollViewer(DependencyObject parent) + { + if (parent == null) return null; + + for (int i = 0; i < VisualTreeHelper.GetChildrenCount(parent); i++) + { + var child = VisualTreeHelper.GetChild(parent, i); + + if (child is ScrollViewer scrollViewer) + { + return scrollViewer; + } + + var result = FindScrollViewer(child); + if (result != null) + { + return result; + } + } + + return null; + } } diff --git a/BetterGenshinImpact/View/Controls/DomainSelector.xaml b/BetterGenshinImpact/View/Controls/DomainSelector.xaml index a20b284a..28043acf 100644 --- a/BetterGenshinImpact/View/Controls/DomainSelector.xaml +++ b/BetterGenshinImpact/View/Controls/DomainSelector.xaml @@ -38,12 +38,16 @@ - - + + Width="350" + PreviewMouseWheel="PopupBorder_PreviewMouseWheel"> @@ -63,10 +68,12 @@ - + ScrollViewer.VerticalScrollBarVisibility="Auto" + ScrollViewer.CanContentScroll="False"> @@ -78,13 +85,14 @@ - + ScrollViewer.VerticalScrollBarVisibility="Auto" + ScrollViewer.CanContentScroll="False"> diff --git a/BetterGenshinImpact/View/Controls/DomainSelector.xaml.cs b/BetterGenshinImpact/View/Controls/DomainSelector.xaml.cs index cbfb54a3..322a6d4a 100644 --- a/BetterGenshinImpact/View/Controls/DomainSelector.xaml.cs +++ b/BetterGenshinImpact/View/Controls/DomainSelector.xaml.cs @@ -1,9 +1,12 @@ +using System; using BetterGenshinImpact.GameTask.AutoTrackPath.Model; using BetterGenshinImpact.GameTask.Common.Element.Assets; using System.Collections.Generic; using System.Linq; using System.Windows; using System.Windows.Controls; +using System.Windows.Input; +using System.Windows.Media; namespace BetterGenshinImpact.View.Controls; @@ -13,6 +16,15 @@ public partial class DomainSelector : UserControl { InitializeComponent(); Countries = MapLazyAssets.Instance.CountryToDomains.Keys.Reverse().ToList(); + Unloaded += OnUnloaded; + } + + /// + /// 控件卸载时清理事件处理器,防止内存泄漏 + /// + private void OnUnloaded(object sender, RoutedEventArgs e) + { + RemoveWindowMouseWheelHandler(); } public List Countries @@ -95,4 +107,112 @@ public partial class DomainSelector : UserControl MainToggle.IsChecked = false; } } + + /// + /// Popup 打开时添加全局滚轮事件拦截 + /// + private void MainPopup_Opened(object sender, EventArgs e) + { + var window = Window.GetWindow(this); + if (window != null) + { + window.PreviewMouseWheel -= Window_PreviewMouseWheel; + window.PreviewMouseWheel += Window_PreviewMouseWheel; + } + } + + /// + /// Popup 关闭时移除全局滚轮事件拦截 + /// + private void MainPopup_Closed(object sender, EventArgs e) + { + RemoveWindowMouseWheelHandler(); + } + + /// + /// 移除窗口级滚轮事件处理器 + /// + private void RemoveWindowMouseWheelHandler() + { + var window = Window.GetWindow(this); + if (window != null) + { + window.PreviewMouseWheel -= Window_PreviewMouseWheel; + } + } + + /// + /// 全局滚轮事件处理,当 Popup 打开时拦截所有滚轮事件 + /// + private void Window_PreviewMouseWheel(object sender, MouseWheelEventArgs e) + { + if (MainPopup.IsOpen) + { + e.Handled = true; + + var scrollViewer1 = FindScrollViewer(CountriesListView); + var scrollViewer2 = FindScrollViewer(DomainsListView); + + if (scrollViewer1 != null && scrollViewer1.IsMouseOver) + { + scrollViewer1.ScrollToVerticalOffset(scrollViewer1.VerticalOffset - e.Delta / 2.0); + return; + } + + if (scrollViewer2 != null && scrollViewer2.IsMouseOver) + { + scrollViewer2.ScrollToVerticalOffset(scrollViewer2.VerticalOffset - e.Delta / 2.0); + return; + } + } + } + + /// + /// 处理 Popup 内的鼠标滚轮事件,防止滚动穿透到外部页面 + /// + private void PopupBorder_PreviewMouseWheel(object sender, MouseWheelEventArgs e) + { + e.Handled = true; + + var scrollViewer1 = FindScrollViewer(CountriesListView); + var scrollViewer2 = FindScrollViewer(DomainsListView); + + if (scrollViewer1 != null && scrollViewer1.IsMouseOver) + { + scrollViewer1.ScrollToVerticalOffset(scrollViewer1.VerticalOffset - e.Delta / 2.0); + return; + } + + if (scrollViewer2 != null && scrollViewer2.IsMouseOver) + { + scrollViewer2.ScrollToVerticalOffset(scrollViewer2.VerticalOffset - e.Delta / 2.0); + return; + } + } + + /// + /// 在视觉树中查找 ScrollViewer + /// + private ScrollViewer? FindScrollViewer(DependencyObject parent) + { + if (parent == null) return null; + + for (int i = 0; i < VisualTreeHelper.GetChildrenCount(parent); i++) + { + var child = VisualTreeHelper.GetChild(parent, i); + + if (child is ScrollViewer scrollViewer) + { + return scrollViewer; + } + + var result = FindScrollViewer(child); + if (result != null) + { + return result; + } + } + + return null; + } }