Если вы можете использовать NET Framework 3.5 и выше, вам не нужно сканировать документ при каждом изменении: просто подпишитесь на событие TextChanged и используйте свойство TextChangedEventArgs.Changes, чтобы получить список изменений.
Всякий раз, когда вы получаете событие TextChanged, перебирайте коллекцию Changes и создайте TextRange из Offset, AddedLength и RemovedLength. Затем разверните этот 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.Paragraph
и range.End.Paragraph
.
Кроме того, для многих ситуаций будет полезно хранить копию всего текста в документе отдельно от самого FlowDocument. Затем, когда вы применяете изменения к этому документу, вы можете обновлять форматирование по мере необходимости, без необходимости перечитывать документ. Обратите внимание, что текст не должен храниться в одном большом массиве, а должен быть разрезан на маленькие кусочки (возможно, около 1000 символов) и доступен через дерево, которое организует куски по индексу. Причина в том, что вставка символа в начале огромного массива очень дорога.