2012-04-18 152 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的開始。

相關問題