我有一个继承自RichTextBox的自定义WPF控件。我希望每当文本发生变化时都能修改richtextbox的FlowDocument。为了演示,让我们说我们有:
<MyCustomRichTextbox>
<FlowDocument>
<Paragraph>This is the first paragraph</Paragraph>
<Paragraph>And this is the second</Paragraph>
</FlowDocument>
</MyCustomRichTextbox>
并且每当文本被更改时(例如控件中有人输入),整个包含的段落都会显示为红色。
在我看来,要实现这一目标需要做两件事:
MyCustomRichTextbox.Document.Blocks
不幸的是,OnTextChanged方法并没有提供一种方法来获得更改的Block。我能够使用LINQ和TextChangedEventArgs.Offset来获取块,但我担心这种方法会因较大的文档而产生不可接受的减速(因为每次输入一个字符时它必须枚举每个块)。是否有更好的方法来获取包含段落?
我知道我可以缓存对&#34; Last modified block&#34;的引用。并检查它是否仍然是被修改的那个,但这在随机访问场景中确实没有帮助。
答案 0 :(得分:1)
如果我正确理解您的问题,您需要突出显示当前选择的包含段落(插入符号的当前位置)。因此,每次选择更改时,您必须获得包含的段落。然后,您只需将Foreground
更改为Brushes.Red
即可。幸运的是,包含Paragraph似乎被TextPointer引用,并且找到它的过程几乎是立即的。 (TextPointer具有名为Paragraph
的属性)。这是详细的代码:
Paragraph p = null;
//Suppose rtb is the name of your RichtTextBox
private void UpdateContainingBlockState() {
if (p != rtb.Selection.Start.Paragraph){
if (p != null) p.Foreground = Brushes.Black;
p = rtb.Selection.Start.Paragraph;
if (p != null) p.Foreground = Brushes.Red;
}
}
//The SelectionChanged event handler for your RichTextBox
private void selectionChangedHandler(object sender, RoutedEventArgs e){
UpdateContainingBlockState();
}
更改选择的频率相当高(每次按几乎键都会导致选择更改)。因此,如果您的文档很大并且在键入时意识到性能不佳,那么现在是时候切换到下一个更复杂的代码了。您也可以尝试使用线程方法(或使用Task)将UpdateContainingBlockState()
调用放在另一个线程中,但要注意跨线程访问。在这里,我使用了不同的方法,想法是在正确的时间调用UpdateContainingBlockState()
,即实际的选择更改可以在段落之间跳转。在输入正常的可打印字符时,选择将始终在当前段落中(因此我们不需要调用UpdateContainingBlockState()
),除非您键入 Enter 键。通常,当用户键入控制键(箭头键,home,end,Enter,...)时,我们将调用该方法。如果RichTextBox被聚焦并且用户在RichTextBox上单击鼠标,我们也应该调用该方法。您可以看到,几乎所有类型的字符都不会触发调用该方法,因此它将比上面的代码更多地提高性能(当然,只有当文档很大时才可以实现)。这是详细的代码:
//should add this using at the beginning
using System.Runtime.InteropServices;
[DllImport("user32")]
private static extern int MapVirtualKey(int ucode, int mapType);
//The KeyUp event handler for your RichTextBox
private void keyUp_Handler(object sender, KeyEventArgs e){
if (char.IsControl((char) MapVirtualKey(KeyInterop.VirtualKeyFromKey(e.Key),0x2)))
UpdateContainingBlockState();
}
//The PreviewMouseUp event handler for your RichTextBox
private void previewMouseUp_Handler(object sender, MouseButtonEventArgs e){
//UpdateContainingBlockState();
//Calling UpdateContainingBlockState() directly will cause a small issue
//So we use this instead
Task.Run(() => Dispatcher.Invoke( () => UpdateContainingBlockState()));
}
//The GotKeyboardFocus event handler for your RichTextBox
private void gotKeyboardFocus_Handler(object sender,
KeyboardFocusChangedEventArgs e){
UpdateContainingBlockState();
}