我正在尝试实现(最初是作为原型),可以实时解析以对其应用某些格式设置选项的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.Paragraph
和range.End.Paragraph
。此外,在许多情况下,将所有文本的副本与FlowDocument本身分开存储将很有帮助。然后,在将更改应用于该文档时,您可以随时更新格式,而不必重新阅读该文档。请注意,不应将文本存储在单个大数组中,而应将其切成小段(可能约1000个字符),并通过按索引组织这些段的树进行访问。原因是在巨大数组的开头插入字符非常昂贵。