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; + } }