2012-04-18 48 views
2

我试图用WPF RichTextEditor实现一个基本的语法高亮显示。为此,我想以不同的颜色显示{} gropus。TextRange GetPositionAtOffset不像预期的那样运行

这里的这应该一个RichTextBox的内容分成不同的组的代码:

List<Tag> SplitIntoParts(TextRange textRange, int level) 
    { 
     if (textRange==null||textRange.IsEmpty) 
     { 
      return new List<Tag>(); 
     } 
     string text = textRange.Text; 
     if (text.Length==0) 
     { 
      return new List<Tag>(); 
     } 
     int startPos=-1, counter = 0; 
     List<Tag> result=new List<Tag>(); 
     for (int i = 0; i < text.Length; i++) 
     { 
      if (text[i]=='{') 
      { 
       if (counter==0) 
       { 
        startPos = i; 
       } 
       counter++; 
      } 
      if (text[i]=='}') 
      { 
       if (counter==1) 
       { 
        Tag t = new Tag() 
           { 
            StartPosition = textRange.Start.GetPositionAtOffset(startPos), 
            EndPosition = textRange.Start.GetPositionAtOffset(i+1), 
            Level = level, 
            Word = text.Substring(startPos,i+1-startPos) 
           }; 
        result.Add(t); 
        var tr=new TextRange(textRange.Start.GetPositionAtOffset(startPos + 1), textRange.Start.GetPositionAtOffset(i)); 
        result.AddRange(SplitIntoParts(tr, level + 1)); 
       } 
       counter--; 
      } 
     } 
     if (counter>0)//some open branches still left 
     { 
      var i = text.Length; 
      Tag t = new Tag() 
      { 
       StartPosition = textRange.Start.GetPositionAtOffset(startPos), 
       EndPosition = textRange.End, 
       Level = level, 
       Word = text.Substring(startPos, i - startPos) 
      }; 
      result.Add(t); 
      result.AddRange(SplitIntoParts(new TextRange(textRange.Start.GetPositionAtOffset(startPos + 1), textRange.Start.GetPositionAtOffset(i - 1)), level + 1)); 
     } 

     return result; 
    } 

在该代码中,我发现textRange.Start.GetPositionAtOffset(startPos + 1)运行异常:

让我们说,该代码已发现以下组:

{test|try} 

,并与下面的代码选中:

​​

(例如, t.Word == '{测试|试}')

当我试图通过递归路过

var tr=new TextRange(textRange.Start.GetPositionAtOffset(startPos + 1), textRange.Start.GetPositionAtOffset(i)); 
result.AddRange(SplitIntoParts(tr, level + 1)); 

,而不是做同样的 “测试|试”,tr.Text ==“{测试“

为什么我得到这种行为,我应该如何处理它?

回答

2

GetPositionAtOffset不只计数(可见)字符。幸运的是,最近我遇到了同样的问题,所以我制定了一个方法,使TextPointer在指定的偏移量(只计数可见字符的偏移量)。首先它可能看起来有点复杂,但它确实不是:-)。作为一个参数,它需要内联(来自富文本框,如RichTextBox.Document.Blocks.FirstBlock.Inlines,它们分别只能获得rtb中第一段的内联,如果有的话)。第二个参数是偏移本身。

建议给第三个参数TextPointer指示内容的开始。如果指定了内联,则会从第一个内联确定起始位置,但在没有内联的情况下会抛出异常,为避免出现此情况,请将内容开始参数设置为RichTextBox.Document.ContentStart。方法如下:

/// <summary> 
    /// Returns the position of the specified offset in the text specified by the inlines. 
    /// </summary> 
    /// <param name="inlines">The inlines which specifies the text.</param> 
    /// <param name="offset">The offset within the text to get the position of.</param> 
    /// <param name="contentStartPosition">The position where the content starts. If null, the position before the start of the first inline will be used. If null and there are no inlines, an exception is thrown.</param> 
    /// <returns>A <see cref="TextPointer"/> indicating the position of the specified offset.</returns> 
    public static TextPointer GetPositionAtOffset(this InlineCollection inlines, int offset, TextPointer contentStartPosition = null) 
    { 
     if (inlines == null) 
      throw new ArgumentNullException(nameof(inlines)); 
     if (!inlines.Any() && contentStartPosition == null)//if no inlines, can't determine start of content 
      throw new ArgumentException("A content start position has to be specified if the inlines collection is empty.", nameof(contentStartPosition)); 

     if (contentStartPosition == null) 
      contentStartPosition = inlines.First().ContentStart.DocumentStart;//if no content start specified, gets it 
     int offsetWithInlineBorders = 0;//collects the value of offset (with inline borders) 
     foreach (var inline in inlines) 
     { 
      int inlineLength = (inline as Run)?.Text.Length ?? (inline is LineBreak ? 1 : 0);//gets the length of the inline (length of a Run is the lengts of its text, length of a LineBreak is 1, other types are ignored) 

      if (inlineLength < offset)//if position specified by the offset is beyond this inline... 
       offsetWithInlineBorders += inlineLength + 2;//...then the whole length is added with the two borders 
      else if (inlineLength == offset)//if position specified by the offset is at the end of this inline... 
       offsetWithInlineBorders += inlineLength + 1;//...then the whole length is added with only the opening border 
      else //inlineLength > value, if the position specified by the offset is within this inline 
      { 
       offsetWithInlineBorders += offset + 1;//...then adds the remaining length (the offset itself), plus the opening border 
       break;//the inlines beyond are not needed 
      } 
      offset -= inlineLength;//substracts the added inline length 
     } 

     return contentStartPosition.GetPositionAtOffset(
      Math.Min(Math.Max(offsetWithInlineBorders, 0), contentStartPosition.GetOffsetToPosition(contentStartPosition.DocumentEnd)));//if the value is not within the boundaries of the text, returns the start or the end of the text 
    } 

好运

0

GetPositionAtOffset对符号进行计数,这可能超过文本插入位置。 参见MSDN

返回的TextPointer到由指定 偏移量,以符号表示的位置,从当前TextPointer的开始。

相关问题