refactor(AutoTranslateInterceptor): 优化自动翻译拦截器的加载与应用机制

- 移除 HomePage 中冗余的 EnableAutoTranslate 属性设置,改为继承属性
- 通过类构造函数注册全局 Loaded 事件处理器,替代在每个元素上单独添加
- 引入请求队列机制,批量处理待应用翻译的元素,避免重复调度
- 扩展属性类型检查,支持 object 类型以处理更多动态内容场景
This commit is contained in:
辉鸭蛋
2026-01-27 03:49:34 +08:00
parent 128a739a8c
commit 7fbedddea4
2 changed files with 107 additions and 11 deletions

View File

@@ -13,12 +13,29 @@ namespace BetterGenshinImpact.View.Behavior
{
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 =
DependencyProperty.RegisterAttached(
"EnableAutoTranslate",
typeof(bool),
typeof(AutoTranslateInterceptor),
new PropertyMetadata(false, OnEnableAutoTranslateChanged));
new FrameworkPropertyMetadata(
false,
FrameworkPropertyMetadataOptions.Inherits,
OnEnableAutoTranslateChanged));
public static void SetEnableAutoTranslate(DependencyObject element, bool value)
=> element.SetValue(EnableAutoTranslateProperty, value);
@@ -33,6 +50,68 @@ namespace BetterGenshinImpact.View.Behavior
typeof(AutoTranslateInterceptor),
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)
{
if (d is not FrameworkElement fe)
@@ -76,16 +155,14 @@ namespace BetterGenshinImpact.View.Behavior
private readonly FrameworkElement _root;
private readonly List<Action> _unsubscribe = new();
private bool _applied;
private readonly RoutedEventHandler _anyLoadedHandler;
private readonly HashSet<ContextMenu> _trackedContextMenus = new();
private readonly HashSet<ToolTip> _trackedToolTips = new();
private readonly HashSet<DependencyObject> _pendingApply = new();
private bool _applyScheduled;
public Scope(FrameworkElement 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)
@@ -131,20 +208,41 @@ namespace BetterGenshinImpact.View.Behavior
_unsubscribe.Clear();
}
private void OnAnyLoaded(object sender, RoutedEventArgs e)
public void RequestApply(DependencyObject obj)
{
if (!_applied)
{
return;
}
if (e.OriginalSource is not DependencyObject obj)
if (!_pendingApply.Add(obj))
{
return;
}
if (_applyScheduled)
{
return;
}
_applyScheduled = true;
_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);
}
@@ -325,7 +423,7 @@ namespace BetterGenshinImpact.View.Behavior
{
var entry = enumerator.Current;
var property = entry.Property;
if (property.PropertyType != typeof(string))
if (property.PropertyType != typeof(string) && property.PropertyType != typeof(object))
{
continue;
}

View File

@@ -7,7 +7,6 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:pages="clr-namespace:BetterGenshinImpact.ViewModel.Pages"
xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml"
xmlns:behavior="clr-namespace:BetterGenshinImpact.View.Behavior"
Title="HomePage"
d:DataContext="{d:DesignInstance Type=pages:HomePageViewModel}"
d:DesignHeight="850"
@@ -16,7 +15,6 @@
ui:Design.Foreground="{DynamicResource TextFillColorPrimaryBrush}"
FontFamily="{StaticResource TextThemeFontFamily}"
Foreground="{DynamicResource TextFillColorPrimaryBrush}"
behavior:AutoTranslateInterceptor.EnableAutoTranslate="True"
mc:Ignorable="d">
<b:Interaction.Triggers>