以编程方式在WPF RichTextBox(FlowDocument)中选择文本范围

我有这个WPF RichTextBox,我想以编程方式选择给定范围的字母/单词并突出显示它。 我试过这个,但它不起作用,可能是因为我没有考虑一些隐藏的FlowDocument标签或类似的东西。 例如,我想选择字母3-8但选择2-6):

var start = MyRichTextBox.Document.ContentStart; var startPos = start.GetPositionAtOffset(3); var endPos = start.GetPositionAtOffset(8); var textRange = new TextRange(startPos,endPos); textRange.ApplyPropertyValue(TextElement.ForegroundProperty, new SolidColorBrush(Colors.Blue)); textRange.ApplyPropertyValue(TextElement.FontWeightProperty, FontWeights.Bold); 

我已经意识到RichTextBox处理比我想象的有点棘手:)

更新:我在MSDN论坛上得到了一些答案: 这个post “dekurver”seid:

您指定的偏移量不是字符偏移量,而是符号偏移量。 你需要做的是获得一个你知道与文本相邻的TextPointer,然后你可以添加字符偏移量。

而“LesterLobo”说:

你将需要循环遍历段落和内联以找到Next,然后在循环中找到它们的偏移量以应用特定文本的所有外观。 请注意,当您编辑文本时会移动,但您的突出显示不会移动,因为它与偏移而不是文本相关联。 但是,您可以创建自定义运行并为其提供突出显示…

如果有人知道他们在FlowDocuments周围的方式,仍然会喜欢看到一些示例代码…

编辑我有一个版本的Kratz VB代码工作,它看起来像这样:

 private static TextPointer GetPoint(TextPointer start, int x) { var ret = start; var i = 0; while (i < x && ret != null) { if (ret.GetPointerContext(LogicalDirection.Backward) == TextPointerContext.Text || ret.GetPointerContext(LogicalDirection.Backward) == TextPointerContext.None) i++; if (ret.GetPositionAtOffset(1, LogicalDirection.Forward) == null) return ret; ret = ret.GetPositionAtOffset(1, LogicalDirection.Forward); } return ret; } 

我这样使用它:

 Colorize(item.Offset, item.Text.Length, Colors.Blue); private void Colorize(int offset, int length, Color color) { var textRange = MyRichTextBox.Selection; var start = MyRichTextBox.Document.ContentStart; var startPos = GetPoint(start, offset); var endPos = GetPoint(start, offset + length); textRange.Select(startPos, endPos); textRange.ApplyPropertyValue(TextElement.ForegroundProperty, new SolidColorBrush(color)); textRange.ApplyPropertyValue(TextElement.FontWeightProperty, FontWeights.Bold); } 

 Public Function GoToPoint(ByVal start As TextPointer, ByVal x As Integer) As TextPointer Dim out As TextPointer = start Dim i As Integer = 0 Do While i < x If out.GetPointerContext(LogicalDirection.Backward) = TextPointerContext.Text Or _ out.GetPointerContext(LogicalDirection.Backward) = TextPointerContext.None Then i += 1 End If If out.GetPositionAtOffset(1, LogicalDirection.Forward) Is Nothing Then Return out Else out = out.GetPositionAtOffset(1, LogicalDirection.Forward) End If Loop Return out End Function 

试试这个,这应该返回给定char偏移量的文本指针。 (抱歉在VB中,但这就是我在工作的......)

试试看:

 var textRange = MyRichTextBox.Selection; var start = MyRichTextBox.Document.ContentStart; var startPos = start.GetPositionAtOffset(3); var endPos = start.GetPositionAtOffset(8); textRange.Select(startPos, endPos); textRange.ApplyPropertyValue(TextElement.ForegroundProperty, new SolidColorBrush(Colors.Blue)); textRange.ApplyPropertyValue(TextElement.FontWeightProperty, FontWeights.Bold); 

我尝试使用KratzVB发布的解决方案,但发现它忽略了换行符。 如果你想计算\ r和\ n符号,那么这段代码应该有效:

 private static TextPointer GetPoint(TextPointer start, int x) { var ret = start; var i = 0; while (ret != null) { string stringSoFar = new TextRange(ret, ret.GetPositionAtOffset(i, LogicalDirection.Forward)).Text; if (stringSoFar.Length == x) break; i++; if (ret.GetPositionAtOffset(i, LogicalDirection.Forward) == null) return ret.GetPositionAtOffset(i-1, LogicalDirection.Forward) } ret=ret.GetPositionAtOffset(i, LogicalDirection.Forward); return ret; } 

顺便提一下(除了我自己以外,这可能是学术上的),如果你在RichTextBox的容器上设置FocusManager.IsFocusScope =“True”,例如Grid,

 ... 

那么你应该可以使用Johan Danforth的Colorize方法而不需要两次调用ApplyPropertyValue,而RichTextBox应该使用默认选择Background和Foreground来突出显示选择。

 private void Colorize(int offset, int length, Color color) { var textRange = MyRichTextBox.Selection; var start = MyRichTextBox.Document.ContentStart; var startPos = GetPoint(start, offset); var endPos = GetPoint(start, offset + length); textRange.Select(startPos, endPos); } 

没有尝试使用RichTextBox,但是在模板化FlowDocumentReader中查找TextBox时它可以很好地工作。 只是为了确保你也可以设置

 ... 

确保RichTextBox在其焦点范围内具有焦点。

当然,这样做的缺点是,如果用户在RichTextBox中单击或执行选择,则您的选择将消失。

我的版本基于cave_dweller的版本

 private static TextPointer GetPositionAtCharOffset(TextPointer start, int numbertOfChars) { var offset = start; int i = 0; string stringSoFar=""; while (stringSoFar.Length < numbertOfChars) { i++; TextPointer offsetCandidate = start.GetPositionAtOffset( i, LogicalDirection.Forward); if (offsetCandidate == null) return offset; // ups.. we are to far offset = offsetCandidate; stringSoFar = new TextRange(start, offset).Text; } return offset; } 

要省略一些字符,请在循环中添加此代码:

 stringSoFar = stringSoFar.Replace("\r\n", "") .Replace(" ", "") 

而不是这个(慢):

 var startPos = GetPoint(start, offset); var endPos = GetPoint(start, offset + length); 

你应该这样做(更快)

 var startPos = GetPoint(start, offset); var endPos = GetPoint(startPos, length); 

或者创建单独的方法来获取TextRange:

 private static TextRange GetTextRange(TextPointer start, int startIndex, int length) { var rangeStart = GetPositionAtCharOffset(start, startIndex); var rangeEnd = GetPositionAtCharOffset(rangeStart, length); return new TextRange(rangeStart, rangeEnd); } 

您现在可以在不使用Select()情况下格式化文本:

 var range = GetTextRange(Document.ContentStart, 3, 8); range.ApplyPropertyValue( TextElement.BackgroundProperty, new SolidColorBrush(Colors.Aquamarine)); 

长时间无法找到具有可接受性能解决方案的解决方案。 下一个示例适用于我的情况,性能最高。 希望它也会对某人有所帮助。

 TextPointer startPos = rtb.Document.ContentStart.GetPositionAtOffset(searchWordIndex, LogicalDirection.Forward); startPos = startPos.CorrectPosition(searchWord, FindDialog.IsCaseSensitive); if (startPos != null) { TextPointer endPos = startPos.GetPositionAtOffset(textLength, LogicalDirection.Forward); if (endPos != null) { rtb.Selection.Select(startPos, endPos); } } public static TextPointer CorrectPosition(this TextPointer position, string word, bool caseSensitive) { TextPointer start = null; while (position != null) { if (position.GetPointerContext(LogicalDirection.Forward) == TextPointerContext.Text) { string textRun = position.GetTextInRun(LogicalDirection.Forward); int indexInRun = textRun.IndexOf(word, caseSensitive ? StringComparison.InvariantCulture : StringComparison.InvariantCultureIgnoreCase); if (indexInRun >= 0) { start = position.GetPositionAtOffset(indexInRun); break; } } position = position.GetNextContextPosition(LogicalDirection.Forward); } return start; } 
  private TextPointer GetPoint(TextPointer start, int pos) { var ret = start; int i = 0; while (i < pos) { if (ret.GetPointerContext(LogicalDirection.Forward) == TextPointerContext.Text) i++; if (ret.GetPositionAtOffset(1, LogicalDirection.Forward) == null) return ret; ret = ret.GetPositionAtOffset(1, LogicalDirection.Forward); } return ret; }