mirror of
https://github.com/babalae/better-genshin-impact.git
synced 2026-05-21 09:45:48 +08:00
refactor(AutoTranslateInterceptor): 优化自动翻译拦截器的加载与应用机制
- 移除 HomePage 中冗余的 EnableAutoTranslate 属性设置,改为继承属性 - 通过类构造函数注册全局 Loaded 事件处理器,替代在每个元素上单独添加 - 引入请求队列机制,批量处理待应用翻译的元素,避免重复调度 - 扩展属性类型检查,支持 object 类型以处理更多动态内容场景
This commit is contained in:
@@ -13,12 +13,29 @@ namespace BetterGenshinImpact.View.Behavior
|
|||||||
{
|
{
|
||||||
public static class AutoTranslateInterceptor
|
public static class AutoTranslateInterceptor
|
||||||
{
|
{
|
||||||
|
static AutoTranslateInterceptor()
|
||||||
|
{
|
||||||
|
EventManager.RegisterClassHandler(
|
||||||
|
typeof(FrameworkElement),
|
||||||
|
FrameworkElement.LoadedEvent,
|
||||||
|
new RoutedEventHandler(OnAnyElementLoaded),
|
||||||
|
true);
|
||||||
|
EventManager.RegisterClassHandler(
|
||||||
|
typeof(FrameworkContentElement),
|
||||||
|
FrameworkContentElement.LoadedEvent,
|
||||||
|
new RoutedEventHandler(OnAnyElementLoaded),
|
||||||
|
true);
|
||||||
|
}
|
||||||
|
|
||||||
public static readonly DependencyProperty EnableAutoTranslateProperty =
|
public static readonly DependencyProperty EnableAutoTranslateProperty =
|
||||||
DependencyProperty.RegisterAttached(
|
DependencyProperty.RegisterAttached(
|
||||||
"EnableAutoTranslate",
|
"EnableAutoTranslate",
|
||||||
typeof(bool),
|
typeof(bool),
|
||||||
typeof(AutoTranslateInterceptor),
|
typeof(AutoTranslateInterceptor),
|
||||||
new PropertyMetadata(false, OnEnableAutoTranslateChanged));
|
new FrameworkPropertyMetadata(
|
||||||
|
false,
|
||||||
|
FrameworkPropertyMetadataOptions.Inherits,
|
||||||
|
OnEnableAutoTranslateChanged));
|
||||||
|
|
||||||
public static void SetEnableAutoTranslate(DependencyObject element, bool value)
|
public static void SetEnableAutoTranslate(DependencyObject element, bool value)
|
||||||
=> element.SetValue(EnableAutoTranslateProperty, value);
|
=> element.SetValue(EnableAutoTranslateProperty, value);
|
||||||
@@ -33,6 +50,68 @@ namespace BetterGenshinImpact.View.Behavior
|
|||||||
typeof(AutoTranslateInterceptor),
|
typeof(AutoTranslateInterceptor),
|
||||||
new PropertyMetadata(null));
|
new PropertyMetadata(null));
|
||||||
|
|
||||||
|
private static void OnAnyElementLoaded(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
if (sender is not DependencyObject obj)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
FindNearestScope(obj)?.RequestApply(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Scope? FindNearestScope(DependencyObject obj)
|
||||||
|
{
|
||||||
|
DependencyObject? current = obj;
|
||||||
|
while (current != null)
|
||||||
|
{
|
||||||
|
if (current is FrameworkElement fe && fe.GetValue(ScopeProperty) is Scope scope)
|
||||||
|
{
|
||||||
|
return scope;
|
||||||
|
}
|
||||||
|
|
||||||
|
current = GetParentObject(current);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static DependencyObject? GetParentObject(DependencyObject obj)
|
||||||
|
{
|
||||||
|
if (obj is FrameworkElement fe)
|
||||||
|
{
|
||||||
|
if (fe.Parent != null)
|
||||||
|
{
|
||||||
|
return fe.Parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fe.TemplatedParent is DependencyObject templatedParent)
|
||||||
|
{
|
||||||
|
return templatedParent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (obj is FrameworkContentElement fce)
|
||||||
|
{
|
||||||
|
if (fce.Parent != null)
|
||||||
|
{
|
||||||
|
return fce.Parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fce.TemplatedParent is DependencyObject templatedParent)
|
||||||
|
{
|
||||||
|
return templatedParent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (obj is Visual || obj is System.Windows.Media.Media3D.Visual3D)
|
||||||
|
{
|
||||||
|
return VisualTreeHelper.GetParent(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
return LogicalTreeHelper.GetParent(obj);
|
||||||
|
}
|
||||||
|
|
||||||
private static void OnEnableAutoTranslateChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
private static void OnEnableAutoTranslateChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||||
{
|
{
|
||||||
if (d is not FrameworkElement fe)
|
if (d is not FrameworkElement fe)
|
||||||
@@ -76,16 +155,14 @@ namespace BetterGenshinImpact.View.Behavior
|
|||||||
private readonly FrameworkElement _root;
|
private readonly FrameworkElement _root;
|
||||||
private readonly List<Action> _unsubscribe = new();
|
private readonly List<Action> _unsubscribe = new();
|
||||||
private bool _applied;
|
private bool _applied;
|
||||||
private readonly RoutedEventHandler _anyLoadedHandler;
|
|
||||||
private readonly HashSet<ContextMenu> _trackedContextMenus = new();
|
private readonly HashSet<ContextMenu> _trackedContextMenus = new();
|
||||||
private readonly HashSet<ToolTip> _trackedToolTips = new();
|
private readonly HashSet<ToolTip> _trackedToolTips = new();
|
||||||
|
private readonly HashSet<DependencyObject> _pendingApply = new();
|
||||||
|
private bool _applyScheduled;
|
||||||
|
|
||||||
public Scope(FrameworkElement root)
|
public Scope(FrameworkElement root)
|
||||||
{
|
{
|
||||||
_root = root;
|
_root = root;
|
||||||
_anyLoadedHandler = OnAnyLoaded;
|
|
||||||
_root.AddHandler(FrameworkElement.LoadedEvent, _anyLoadedHandler, true);
|
|
||||||
_unsubscribe.Add(() => _root.RemoveHandler(FrameworkElement.LoadedEvent, _anyLoadedHandler));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnLoaded(object sender, RoutedEventArgs e)
|
public void OnLoaded(object sender, RoutedEventArgs e)
|
||||||
@@ -131,20 +208,41 @@ namespace BetterGenshinImpact.View.Behavior
|
|||||||
_unsubscribe.Clear();
|
_unsubscribe.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnAnyLoaded(object sender, RoutedEventArgs e)
|
public void RequestApply(DependencyObject obj)
|
||||||
{
|
{
|
||||||
if (!_applied)
|
if (!_applied)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (e.OriginalSource is not DependencyObject obj)
|
if (!_pendingApply.Add(obj))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_applyScheduled)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_applyScheduled = true;
|
||||||
_root.Dispatcher.BeginInvoke(
|
_root.Dispatcher.BeginInvoke(
|
||||||
() => Apply(obj),
|
() =>
|
||||||
|
{
|
||||||
|
_applyScheduled = false;
|
||||||
|
if (!_applied)
|
||||||
|
{
|
||||||
|
_pendingApply.Clear();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var items = _pendingApply.ToArray();
|
||||||
|
_pendingApply.Clear();
|
||||||
|
foreach (var item in items)
|
||||||
|
{
|
||||||
|
Apply(item);
|
||||||
|
}
|
||||||
|
},
|
||||||
DispatcherPriority.Loaded);
|
DispatcherPriority.Loaded);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -325,7 +423,7 @@ namespace BetterGenshinImpact.View.Behavior
|
|||||||
{
|
{
|
||||||
var entry = enumerator.Current;
|
var entry = enumerator.Current;
|
||||||
var property = entry.Property;
|
var property = entry.Property;
|
||||||
if (property.PropertyType != typeof(string))
|
if (property.PropertyType != typeof(string) && property.PropertyType != typeof(object))
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,6 @@
|
|||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:pages="clr-namespace:BetterGenshinImpact.ViewModel.Pages"
|
xmlns:pages="clr-namespace:BetterGenshinImpact.ViewModel.Pages"
|
||||||
xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml"
|
xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml"
|
||||||
xmlns:behavior="clr-namespace:BetterGenshinImpact.View.Behavior"
|
|
||||||
Title="HomePage"
|
Title="HomePage"
|
||||||
d:DataContext="{d:DesignInstance Type=pages:HomePageViewModel}"
|
d:DataContext="{d:DesignInstance Type=pages:HomePageViewModel}"
|
||||||
d:DesignHeight="850"
|
d:DesignHeight="850"
|
||||||
@@ -16,7 +15,6 @@
|
|||||||
ui:Design.Foreground="{DynamicResource TextFillColorPrimaryBrush}"
|
ui:Design.Foreground="{DynamicResource TextFillColorPrimaryBrush}"
|
||||||
FontFamily="{StaticResource TextThemeFontFamily}"
|
FontFamily="{StaticResource TextThemeFontFamily}"
|
||||||
Foreground="{DynamicResource TextFillColorPrimaryBrush}"
|
Foreground="{DynamicResource TextFillColorPrimaryBrush}"
|
||||||
behavior:AutoTranslateInterceptor.EnableAutoTranslate="True"
|
|
||||||
mc:Ignorable="d">
|
mc:Ignorable="d">
|
||||||
|
|
||||||
<b:Interaction.Triggers>
|
<b:Interaction.Triggers>
|
||||||
|
|||||||
Reference in New Issue
Block a user