mirror of
https://github.com/babalae/better-genshin-impact.git
synced 2026-03-15 07:43:20 +08:00
feat: JS级联选择 和 秘境选择 滚轮事件禁止穿透至其他窗口 (#2828)
This commit is contained in:
@@ -44,7 +44,9 @@
|
||||
PlacementTarget="{Binding ElementName=MainToggle}"
|
||||
StaysOpen="False"
|
||||
AllowsTransparency="True"
|
||||
PopupAnimation="Slide">
|
||||
PopupAnimation="Slide"
|
||||
Opened="MainPopup_Opened"
|
||||
Closed="MainPopup_Closed">
|
||||
<Border x:Name="PopupBorder"
|
||||
Background="{DynamicResource ApplicationBackgroundBrush}"
|
||||
BorderBrush="{DynamicResource SurfaceStrokeColorDefaultBrush}"
|
||||
@@ -55,7 +57,8 @@
|
||||
MinHeight="100"
|
||||
MaxHeight="300"
|
||||
MinWidth="120"
|
||||
MaxWidth="600">
|
||||
MaxWidth="600"
|
||||
PreviewMouseWheel="PopupBorder_PreviewMouseWheel">
|
||||
<Border.Effect>
|
||||
<DropShadowEffect BlurRadius="10" ShadowDepth="2" Direction="270" Color="Black" Opacity="0.2"/>
|
||||
</Border.Effect>
|
||||
@@ -70,7 +73,8 @@
|
||||
ItemsSource="{Binding FirstLevelOptions, ElementName=Root}"
|
||||
BorderThickness="0"
|
||||
SelectionChanged="FirstLevelListView_SelectionChanged"
|
||||
ScrollViewer.VerticalScrollBarVisibility="Auto">
|
||||
ScrollViewer.VerticalScrollBarVisibility="Auto"
|
||||
ScrollViewer.CanContentScroll="False">
|
||||
<ui:ListView.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock Text="{Binding}" Margin="8,4"/>
|
||||
@@ -86,7 +90,8 @@
|
||||
ItemsSource="{Binding SecondLevelOptions, ElementName=Root}"
|
||||
BorderThickness="0"
|
||||
SelectionChanged="SecondLevelListView_SelectionChanged"
|
||||
ScrollViewer.VerticalScrollBarVisibility="Auto">
|
||||
ScrollViewer.VerticalScrollBarVisibility="Auto"
|
||||
ScrollViewer.CanContentScroll="False">
|
||||
<ui:ListView.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock Text="{Binding}" Margin="8,4"/>
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 控件卸载时清理事件处理器,防止内存泄漏
|
||||
/// </summary>
|
||||
private void OnUnloaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
RemoveWindowMouseWheelHandler();
|
||||
}
|
||||
|
||||
public Dictionary<string, List<string>>? CascadeOptions
|
||||
{
|
||||
get { return (Dictionary<string, List<string>>?)GetValue(CascadeOptionsProperty); }
|
||||
@@ -277,4 +288,112 @@ public partial class CascadeSelector : UserControl
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Popup 打开时添加全局滚轮事件拦截
|
||||
/// </summary>
|
||||
private void MainPopup_Opened(object sender, EventArgs e)
|
||||
{
|
||||
var window = Window.GetWindow(this);
|
||||
if (window != null)
|
||||
{
|
||||
window.PreviewMouseWheel -= Window_PreviewMouseWheel;
|
||||
window.PreviewMouseWheel += Window_PreviewMouseWheel;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Popup 关闭时移除全局滚轮事件拦截
|
||||
/// </summary>
|
||||
private void MainPopup_Closed(object sender, EventArgs e)
|
||||
{
|
||||
RemoveWindowMouseWheelHandler();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 移除窗口级滚轮事件处理器
|
||||
/// </summary>
|
||||
private void RemoveWindowMouseWheelHandler()
|
||||
{
|
||||
var window = Window.GetWindow(this);
|
||||
if (window != null)
|
||||
{
|
||||
window.PreviewMouseWheel -= Window_PreviewMouseWheel;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 全局滚轮事件处理,当 Popup 打开时拦截所有滚轮事件
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 处理 Popup 内的鼠标滚轮事件,防止滚动穿透到外部页面
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 在视觉树中查找 ScrollViewer
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,12 +38,16 @@
|
||||
</ToggleButton.Template>
|
||||
</ToggleButton>
|
||||
|
||||
<Popup IsOpen="{Binding IsChecked, ElementName=MainToggle, Mode=TwoWay}"
|
||||
<Popup x:Name="MainPopup"
|
||||
IsOpen="{Binding IsChecked, ElementName=MainToggle, Mode=TwoWay}"
|
||||
PlacementTarget="{Binding ElementName=MainToggle}"
|
||||
StaysOpen="False"
|
||||
AllowsTransparency="True"
|
||||
PopupAnimation="Slide">
|
||||
<Border Background="{DynamicResource ApplicationBackgroundBrush}"
|
||||
PopupAnimation="Slide"
|
||||
Opened="MainPopup_Opened"
|
||||
Closed="MainPopup_Closed">
|
||||
<Border x:Name="PopupBorder"
|
||||
Background="{DynamicResource ApplicationBackgroundBrush}"
|
||||
BorderBrush="{DynamicResource SurfaceStrokeColorDefaultBrush}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="8"
|
||||
@@ -51,7 +55,8 @@
|
||||
Padding="4"
|
||||
MinHeight="200"
|
||||
MaxHeight="400"
|
||||
Width="350">
|
||||
Width="350"
|
||||
PreviewMouseWheel="PopupBorder_PreviewMouseWheel">
|
||||
<Border.Effect>
|
||||
<DropShadowEffect BlurRadius="10" ShadowDepth="2" Direction="270" Color="Black" Opacity="0.2"/>
|
||||
</Border.Effect>
|
||||
@@ -63,10 +68,12 @@
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<!-- Countries -->
|
||||
<ui:ListView ItemsSource="{Binding Countries, ElementName=Root}"
|
||||
<ui:ListView x:Name="CountriesListView"
|
||||
ItemsSource="{Binding Countries, ElementName=Root}"
|
||||
SelectedItem="{Binding SelectedCountry, ElementName=Root, Mode=TwoWay}"
|
||||
BorderThickness="0"
|
||||
ScrollViewer.VerticalScrollBarVisibility="Auto">
|
||||
ScrollViewer.VerticalScrollBarVisibility="Auto"
|
||||
ScrollViewer.CanContentScroll="False">
|
||||
<ui:ListView.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock Text="{Binding}" Margin="8,4"/>
|
||||
@@ -78,13 +85,14 @@
|
||||
<Rectangle Grid.Column="1" Width="1" Fill="{DynamicResource SurfaceStrokeColorDefaultBrush}" Margin="2,0"/>
|
||||
|
||||
<!-- Domains -->
|
||||
<ui:ListView Grid.Column="2"
|
||||
<ui:ListView Grid.Column="2" x:Name="DomainsListView"
|
||||
ItemsSource="{Binding FilteredDomains, ElementName=Root}"
|
||||
SelectedValue="{Binding SelectedDomain, ElementName=Root, Mode=TwoWay}"
|
||||
SelectedValuePath="Item2.Name"
|
||||
BorderThickness="0"
|
||||
SelectionChanged="DomainListView_SelectionChanged"
|
||||
ScrollViewer.VerticalScrollBarVisibility="Auto">
|
||||
ScrollViewer.VerticalScrollBarVisibility="Auto"
|
||||
ScrollViewer.CanContentScroll="False">
|
||||
<ui:ListView.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock Margin="8,4">
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 控件卸载时清理事件处理器,防止内存泄漏
|
||||
/// </summary>
|
||||
private void OnUnloaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
RemoveWindowMouseWheelHandler();
|
||||
}
|
||||
|
||||
public List<string> Countries
|
||||
@@ -95,4 +107,112 @@ public partial class DomainSelector : UserControl
|
||||
MainToggle.IsChecked = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Popup 打开时添加全局滚轮事件拦截
|
||||
/// </summary>
|
||||
private void MainPopup_Opened(object sender, EventArgs e)
|
||||
{
|
||||
var window = Window.GetWindow(this);
|
||||
if (window != null)
|
||||
{
|
||||
window.PreviewMouseWheel -= Window_PreviewMouseWheel;
|
||||
window.PreviewMouseWheel += Window_PreviewMouseWheel;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Popup 关闭时移除全局滚轮事件拦截
|
||||
/// </summary>
|
||||
private void MainPopup_Closed(object sender, EventArgs e)
|
||||
{
|
||||
RemoveWindowMouseWheelHandler();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 移除窗口级滚轮事件处理器
|
||||
/// </summary>
|
||||
private void RemoveWindowMouseWheelHandler()
|
||||
{
|
||||
var window = Window.GetWindow(this);
|
||||
if (window != null)
|
||||
{
|
||||
window.PreviewMouseWheel -= Window_PreviewMouseWheel;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 全局滚轮事件处理,当 Popup 打开时拦截所有滚轮事件
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 处理 Popup 内的鼠标滚轮事件,防止滚动穿透到外部页面
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 在视觉树中查找 ScrollViewer
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user