首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >TextRange GetPositionAtOffset的行为与预期不符

TextRange GetPositionAtOffset的行为与预期不符
EN

Stack Overflow用户
提问于 2012-04-18 23:24:51
回答 2查看 1.3K关注 0票数 3

我正在尝试使用WPF RichTextEditor实现一个基本的语法高亮显示。为此,我想用不同的颜色显示{} gropu。

下面是将RichTextBox的内容分成不同组的代码:

代码语言:javascript
复制
    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)的行为很奇怪:

比方说,代码找到了以下组:

代码语言:javascript
复制
{test|try}

并使用以下代码选择它:

代码语言:javascript
复制
var t=new Tag()
                                {
                                    StartPosition = textRange.Start.GetPositionAtOffset(startPos), 
                                    EndPosition = textRange.Start.GetPositionAtOffset(i+1), 
                                    Level = level,
                                    Word = text.Substring(startPos,i+1-startPos)
                                };

(例如,测试‘{t.Word==|try}’)

当我尝试通过传递递归方式执行相同的操作时

代码语言:javascript
复制
var tr=new TextRange(textRange.Start.GetPositionAtOffset(startPos + 1), textRange.Start.GetPositionAtOffset(i));
result.AddRange(SplitIntoParts(tr, level + 1));

不是“tr.Text |try”,而是“{==”

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

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2017-02-21 06:26:19

GetPositionAtOffset并不只计算(可见的)字符。幸运的是,我最近遇到了同样的问题,所以我做了一个方法,在指定的偏移量(只计算可见字符的偏移量)处获取TextPointer。首先,它可能看起来有点复杂,但实际上并非如此:-)。

作为一个参数,它需要内联(从富文本框中,例如RichTextBox.Document.Blocks.FirstBlock.Inlines,如果有的话,它只获取rtb中第一个段落的内联);第二个参数是偏移量本身。

建议提供第三个参数,即指示内容开始的TextPointer。如果指定了inline,则从第一个inline确定开始位置,但如果没有inline,则会抛出异常。要避免这种情况,请将content start参数设置为RichTextBox.Document.ContentStart。方法如下:

代码语言:javascript
复制
    /// <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
    }

祝好运

票数 3
EN

Stack Overflow用户

发布于 2013-04-06 00:12:53

GetPositionAtOffset对符号进行计数,这些符号可能多于文本插入位置。请参阅MSDN

将TextPointer返回到从当前TextPointer的开头开始到指定偏移量(以符号为单位)指示的位置。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/10212662

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档