在WPF中实现解析/可编辑Richtextbox的最佳方法 [英] Best way to implement a Parsing/Editable Richtextbox in WPF

查看:536
本文介绍了在WPF中实现解析/可编辑Richtextbox的最佳方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试实现(最初是作为原型)一个Richtextbox控件,该控件可以实时解析以对其应用某些格式设置选项.这是在WPF中完成的,因此我认为最好的方法是扩展现有的RTF文本框控件.

I'm trying to implement (as a prototype initially), a richtextbox control which can be parsed in real time to apply certain formatting options to it. This is being done in WPF so I thought the best way to go about it would be to extend the existing rich text box control.

我遇到了一个问题,即它的文档编写得不好,示例也很慢(我发现这些示例在每次击键时都会解析整个文档).

I've run into the issue where it isn't documented well and the examples are quite slow (the ones I found have parse the whole document on every keystroke).

我目前决定解决的方法是创建一个自定义Inline元素,该元素可以容纳格式标记和内容.因此,我只需解析当前段落和该段落中的运行即可格式化标签.

The way I've currently decided to go about it is to create a custom Inline element which can hold the formatting tags, and the contents. Hence I only have to parse the current paragraph and the runs in that paragraph for formatting tags.

是否有更好的方法来实现这一目标?请注意,这不适用于基于代码/语法的文档(因此AvalonEdit不适用).

Is there a better way to achieve this? Note this isn't for code/syntax based documents (so AvalonEdit isn't appropriate).

欢呼

推荐答案

如果您可以针对NET Framework 3.5及更高版本,则无需在每次更改时都扫描文档:只需订阅TextChanged事件并使用TextChangedEventArgs .Changes属性以获取更改列表.

If you can target NET Framework 3.5 and above, you don't need to scan the document on every change: Just subscribe to the TextChanged event and use the TextChangedEventArgs.Changes property to get a list of changes.

每当您收到TextChanged事件时,请遍历Changes集合并根据Offset,AddedLength和RemovedLength构造TextRange.然后适当地扩展此TextRange以重新计算格式,然后执行格式计算并作为一个单独的步骤进行更新(在Dispatcher.BeginInvoke回调中),这样您就不必再遇到递归TextChanged事件了.

Whenever you receive a TextChanged event, iterate through the Changes collection and construct a TextRange from the Offset, AddedLength, and RemovedLength. Then expand this TextRange as appropriate for recalculating formatting, then do the formatting calculation and update as a separate step (in a Dispatcher.BeginInvoke callback) so you don't end up having recursive TextChanged events.

richTextBox.TextChanged += (obj, e)
{
  var document = richTextBox.Document;
  var length = document.ContentStart.GetOffsetToPosition(document.ContentEnd);
  int totalAdd = 0;
  int totalRemove = 0;
  foreach(var change in e.Changes)
  {
    var expandBy = Math.Max(totalAdd,totalRemove);

    var startIndex = change.Offset - expandBy;
    var endIndex = changed.Offset + expandBy + Math.Max(totalAdd, totalRemove);

    startIndex = Math.Max(startIndex, 0);
    endIndex = Math.Min(endIndex, length);

    var startPointer = document.ContentStart.GetPositionAtOffset(startIndex);
    var endPointer = startPointer.GetPositionAtOffset(endIndex - startIndex);

    var range = new TextRange(startPointer, endPointer);
    Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() =>
    {
      DoParsingAndFormatting(ExpandRangeToUnitOfParsing(range));
    });
    totalAdd += change.AddedLength;
    totalRemove += change.RemovedLength;
  }
};

如果要查找更改开始或结束的段落,可以使用range.Start.Paragraphrange.End.Paragraph.

If you want to find the paragraph where a change begins or ends, you can use range.Start.Paragraph and range.End.Paragraph.

此外,在许多情况下,将所有文本的副本与FlowDocument本身分开存储将是有帮助的.然后,在将更改应用于该文档时,您可以随时更新格式,而不必重新阅读该文档.请注意,不应将文本存储在单个大数组中,而应将其切成小块(可能约1000个字符),并通过按索引组织这些块的树进行访问.原因是在巨大数组的开头插入字符非常昂贵.

Also, for many situations it will be helpful to store a copy of all the text in the document separately from the FlowDocument itself. Then as you apply changes to that document you can update the formatting as you go without having to reread the document. Note that the text should not be stored in a single large array, but rather snipped into small pieces (perhaps around 1000 characters) and accessed through a tree that organizes the pieces by index. The reason is that inserting a character at the beginning of a huge array is very expensive.

这篇关于在WPF中实现解析/可编辑Richtextbox的最佳方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆