这两天给我的窗边岛项目实现了 X 岛揭示板网页中的防剧透功能。这个功能本质上就是,当鼠标悬浮在文字上时显示原本的内容,当鼠标移出文字时则用黑块代替。即
| X 岛揭示板 | 窗边岛 | 
|---|---|
![]()  | 
![]()  | 
虽然说起来很简单,但是好像网上并没有针对这个需求有什么相关的内容,所以在这里记录下我的实现,权当抛砖引玉。
背景
在 X岛揭示板中,防剧透是通过 [h][/h] 这个自定义标签实现的,所以上面 GIF 图中的文字其实是正常文字--[h]防剧透文字[/h]--正常文字--[h]防剧透文字[/h]--。那么这里要做的就有两件事:解析这个自定义标签,以及在 TextBlock 控件中实现黑块和正常文字的互相替换。
为 TextBlock 对象填充内容
TextBlock 对象有两种填充内容的方式:
直接将内容放入
TextBlock.Text属性中。这种方式适合不包含防剧透标签的内容。1
2
3
4
5
6
7
8new TextBlock
{
VerticalAlignment = VerticalAlignment.Top,
HorizontalAlignment = HorizontalAlignment.Stretch,
Text = content,
TextWrapping = TextWrapping.Wrap,
IsTextSelectionEnabled = textSelectionEnabled,
};将内容分散到各个
Run对象中,并将这些Run对象放在TextBlock.Inlines属性中。我就是搭配这种方式实现的防剧透功能。1
2
3
4
5
6
7
8
9
10
11
12
13var run1 = new Run { Text = "Run 1" };
var run2 = new Run { Text = "Run 2" };
var textBlock = new TextBlock
{
VerticalAlignment = VerticalAlignment.Top,
HorizontalAlignment = HorizontalAlignment.Stretch,
TextWrapping = TextWrapping.Wrap,
IsTextSelectionEnabled = textSelectionEnabled,
};
textBlock.Inlines.Add(run1);
textBlock.Inlines.Add(run2);
保存黑块下的原本内容
因为防剧透本质就是,平时用黑块替换掉要遮挡的内容,仅在鼠标悬浮时再用真正的内容替换掉黑块,所以我们需要一个地方来保存原本的内容。本来我想直接在 Run 对象上下功夫,但是可惜 Run 不像 TextBlock 有一个 DataContext 属性可以放东西,所以最后我还是把目光放在了 TextBlock 上。
TextBlock.DataContext 是一个 object 类型的属性,所以我们可以随意放任何我们想放的东西。
当然为了扩展性考虑,我们最好还是给它创建一个类。
1  | class TextBlockDataContext  | 
然后我在给一个段落创建 TextBlock 时,就可以把这个 TextBlockDataContext 对象放在 DataContext 属性中备用。
1  | textBlock = new TextBlock  | 
解析标签并生成 Run 对象
这部分的思路就是,整行文字会被 [h] 和 [/h] 标签切割成各自的 Run,因为 TextBlock.Inlines 是一个有序的列表,所以在切割和生成 Run 对象时,我可以在 TextBlockDataContext.IndexAndOriginalTextOfHiddenContent 中记录下要防剧透的 Run 的下标和它实际的内容。同时,针对要防剧透的 Run,我先用黑块字符█填充它的 Text 属性。
1  | int indexOfRun = 0;  | 
实现鼠标悬浮时显示真实内容
TextBlock 提供了两个事件 PointerEntered 和 PointerExited,分别对应鼠标指针进入和离开 TextBlock 范围。所以我们就可以给这两个事件分别绑定 UnhidingContent 方法和 HidingContent 方法来实现鼠标悬浮时显示真正内容。
1  | private static void HidingContent(object sender, PointerRoutedEventArgs pointerRoutedEventArgs)  | 
至此,与 X 岛揭示板网页端类似的防剧透功能就完成实现了。完整的代码可以参考对应的 GitHub commit。


