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

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

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

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

干杯

最佳答案

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

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

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

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

10-04 18:35