3

WPF RichTextEditorを使用して、基本的な構文の強調表示を実装しようとしています。このために、{}gropusをさまざまな色で表示したいと思います。

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}

次のコードで選択しました。

var t=new Tag()
                                {
                                    StartPosition = textRange.Start.GetPositionAtOffset(startPos), 
                                    EndPosition = textRange.Start.GetPositionAtOffset(i+1), 
                                    Level = level,
                                    Word = text.Substring(startPos,i+1-startPos)
                                };

(egtWord =='{test | try}')

通過することで同じことを再帰的に行おうとすると

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

「test|try」の代わりに、tr.Text == "{test"

なぜこの動作が発生するのですか?また、どのように対処する必要がありますか?

4

2 に答える 2

3

GetPositionAtOffset(表示されている)文字だけをカウントしません。幸いなことに、最近同じ問題が発生したのでTextPointer、指定されたオフセット(表示されている文字のみをカウントするオフセット)でを取得するメソッドを作成しました。最初は少し複雑に見えるかもしれませんが、実際にはそうではありません:-)。

パラメータとして、インラインが必要です(のように、リッチテキストボックスからRichTextBox.Document.Blocks.FirstBlock.Inlines、これはそれぞれrtbの最初の段落のインラインのみを取得します...); 2番目のパラメーターはオフセット自体です。

TextPointerコンテンツの開始を示す3番目のパラメーターを指定することをお勧めします。インラインが指定されている場合、開始位置は最初のインラインから決定されますが、インラインがない場合は例外がスローされます。これを回避するには、コンテンツの開始パラメーターをに設定します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
    }

幸運を

于 2017-02-20T22:26:18.513 に答える
0

GetPositionAtOffset記号をカウントします。これは、テキストの挿入位置よりも多い場合があります。MSDNを参照してください:

現在のTextPointerの先頭から、指定されたオフセットで示される位置に記号でTextPointerを返します。

于 2013-04-05T16:12:52.877 に答える