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

问题描述:

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

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论坛上几个答案:This thread其中“dekurver” SEID:

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

和“LesterLobo”说:

您需要遍历 段落和行内找到 下一步,然后他们的偏移在循环 ,申请 的悉数亮相特定的文本。请注意,编辑 时,文字会移动,但您的 高光不会移动为与 文字相关的偏移而不是 。然而,你可以创建一个自定义的 运行和 它提供的一大亮点......

仍旧爱看到一些这方面的示例代码,如果有人知道他们的周围FlowDocuments方式...

编辑我有一个版本的克拉茨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 

试试这个,这应该返回文本指针定焦偏移。 (对不起,它在VB中,但多数民众赞成在我的工作...)

+0

不错!我有一个代码工作的版本,将其添加到问题中。干杯。 – 2009-09-28 14:48:27

+0

这对于计算RichTextBox中的字符也很方便:只需执行循环,而“out”不为空并在最后返回“i”。 – devios1 2010-08-09 01:18:56

+0

这种方法让我每一个字符后,我的任何指定的令牌后,我只需要它得到一个指向我的单词的指针,因为当我尝试使它粗体例如它使整个句子后我的令牌“大胆”我只需要它来操作我的代币! – a7madx7 2013-07-23 16:42:44

试一下:

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); 
+0

似乎不正常恐怕工作。 – 2009-09-21 13:42:17

+0

我只是试过,它适用于我... – 2009-09-21 15:16:57

+0

@Tomas不适合我恐怕。使用该代码为我选择/着色字母2-6。我要去尝试别的东西,然后回到这里。 – 2009-09-21 18:08:36

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

<Grid FocusManager.IsFocusScope="True">...</Grid> 

,那么你应该能够使用约翰丹福斯的彩色化方法,无需ApplyPropertyValue的两个调用,并在RichTextBox应该使用默认选择背景和前景突出的选择。

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一个发现文本框时。只是为了确保您也可以设置

<RichTextBox FocusManager.FocusedElement="{Binding RelativeSource={RelativeSource Self}}">...</RichTextBox> 

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

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

我尝试使用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; 
} 
+0

这适用于我 - 您需要在“return ret.GetPositionAtOffset(i-1,LogicalDirection.Forward)”之后添加一个分号。我试图编辑但编辑短于6个字符(facepalm) – 2015-11-03 15:21:52

我的版本的基础上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() ING格式的文本:

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; 
    } 
+0

欢迎来到SO并感谢您发布答案。请考虑扩展您的答案以包含您的代码的解释。 – 2016-02-11 18:47:19