-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathTableSortDisabler.cs
More file actions
151 lines (130 loc) · 5.09 KB
/
Copy pathTableSortDisabler.cs
File metadata and controls
151 lines (130 loc) · 5.09 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
using System.Windows;
using Cvte.EasiNote;
using Cvte.Paint.Framework;
using Cvte.Paint.Table;
// UseWindowsForms 会使 SDK 注入 global using System.Windows.Forms,
// 导致 Application 与 System.Windows.Application 歧义,这里显式指定 WPF 版本。
using Application = System.Windows.Application;
namespace DisableTableSortPlugin;
/// <summary>
/// 在授课(Display)模式下移除表格元素的排序功能。
///
/// 排序功能宿主实现位于 Cvte.Paint.Table.dll,包含两条触发路径:
/// 1. 单元格点击 → CellOperation.CellService_CellClick → SortManager.SortAuto
/// (内部私有处理器,通过 UIElement.AddHandler 订阅 CellClickedEvent 路由事件,
/// 且未使用 handledEventsToo,因此尊重 RoutedEventArgs.Handled);
/// 2. 悬停 → TableElement.TableElement_OnMouseMove → SortManager.ShowSortCtroller,
/// 显示升序 / 降序箭头(SortController),点击箭头再次排序。
///
/// 本实现通过以下两条公开 API 组合,完整移除排序:
/// - 用 EventManager.RegisterClassHandler 为 TableCell.CellClickedEvent 注册类处理器,
/// 在授课模式下将点击标记为已处理,阻止宿主点击处理器执行,从而取消数据重排;
/// - 通过公开的 ITableElement.SortCtrl 获取排序箭头控件并持续隐藏,
/// 阻止悬停显示的箭头被点击触发排序。
/// </summary>
internal static class TableSortDisabler
{
/// <summary>
/// 已处理的排序控制器。所有访问都发生在 WPF UI 线程(Dispatcher),
/// 因此无需额外的并发保护。
/// </summary>
private static readonly HashSet<SortController> SuppressedControllers = new();
private static bool _started;
public static void Start()
{
if (_started)
{
return;
}
_started = true;
// 插件默认在后台线程执行;EN.DisplayingBoardApi 需在应用就绪后才能访问,
// 因此先等待 Ready(与官方示例一致)。
if (EN.App.IsReady)
{
Initialize();
}
else
{
EN.App.Ready += OnAppReady;
}
}
private static void OnAppReady(object? sender, EventArgs e)
{
EN.App.Ready -= OnAppReady;
Initialize();
}
private static void Initialize()
{
// 拦截表格单元格点击:在授课模式下置为已处理,
// 阻止 CellOperation.CellService_CellClick 执行(避免 SortAuto 重排)。
// 类处理器注册属于 WPF 操作,需在 UI 线程执行。
Application.Current.Dispatcher.InvokeAsync(() =>
{
EventManager.RegisterClassHandler(
typeof(TableCell),
TableCell.CellClickedEvent,
new EventHandler<CellRoutedEventArgs>(OnCellClicked));
});
// 进入授课模式时隐藏排序箭头。
EN.App.DisplayStarted += (_, _) => ApplyToCurrentSlide();
// 授课过程中切换页面时隐藏排序箭头。
EN.DisplayingBoardApi.CurrentSlideChanged += (_, _) => ApplyToCurrentSlide();
// 页面内容异步加载完成后再次隐藏,覆盖表格延迟加载的情况。
EN.DisplayingBoardApi.SlideLoaded += (_, _) => ApplyToCurrentSlide();
// 若插件加载时已处于授课模式,立即处理一次。
if (EN.App.CurrentMode == AppMode.Display)
{
ApplyToCurrentSlide();
}
}
private static void OnCellClicked(object? sender, CellRoutedEventArgs e)
{
// 仅在授课模式下拦截;备课模式保留正常的单元格选中与编辑。
if (EN.App.CurrentMode == AppMode.Display)
{
e.Handled = true;
}
}
private static void ApplyToCurrentSlide()
{
Application.Current.Dispatcher.InvokeAsync(() =>
{
var slide = EN.DisplayingBoardApi.CurrentSlide;
if (slide is null)
{
return;
}
SuppressSlide(slide);
});
}
private static void SuppressSlide(Slide slide)
{
foreach (var table in slide.Elements.OfType<TableElement>())
{
SuppressTableSort(table);
}
}
private static void SuppressTableSort(TableElement table)
{
var sortController = table.SortCtrl;
if (sortController is null)
{
return;
}
// 每个排序控制器只处理一次,避免重复订阅可见性事件。
if (!SuppressedControllers.Add(sortController))
{
return;
}
sortController.Visibility = Visibility.Collapsed;
sortController.IsHitTestVisible = false;
// 宿主在悬停时会重新显示排序控制器,这里在其变为可见时立即再次隐藏。
sortController.IsVisibleChanged += (sender, args) =>
{
if (sender is SortController controller && args.NewValue is true)
{
controller.Visibility = Visibility.Collapsed;
}
};
}
}