【问题标题】:RichTextBox is finding my search terms in funny placesRichTextBox 在有趣的地方寻找我的搜索词
【发布时间】:2014-02-15 13:00:41
【问题描述】:

我正在尝试在 WPF RichTextBox 中查找字符串的实例。我现在几乎可以使用,但它突出显示了文档的错误部分。

private int curSearchLocation;

private void FindNext_Click(object sender, RoutedEventArgs e)
{
    TextRange text = new TextRange(RichEditor.Document.ContentStart, RichEditor.Document.ContentEnd);
    var location = text.Text.IndexOf(SearchBox.Text, curSearchLocation, StringComparison.CurrentCultureIgnoreCase);
    if (location < 0)
    {
        location = text.Text.IndexOf(SearchBox.Text, StringComparison.CurrentCultureIgnoreCase);
    }
    if (location >= 0)
    {
        curSearchLocation = location + 1;
        RichEditor.Selection.Select(text.Start.GetPositionAtOffset(location), text.Start.GetPositionAtOffset(location + SearchBox.Text.Length));
    }
    else
    {
        curSearchLocation = 0;
        MessageBox.Show("Not found");
    }
    RichEditor.Focus();
}

这就是我搜索“文档”时发生的情况:

这是因为GetPositionAtOffset在其偏移量中包含了非文本元素,例如开始和结束标记,这不是我想要的。我找不到忽略这些元素的方法,也找不到直接获取TextPointer到我想要的文本的方法,这也可以解决问题。

如何让它突出显示正确的文本?

【问题讨论】:

  • 我同意...这是一个有趣的地方寻找匹配。
  • 我会仔细看看您对 SearchBox 元素的绑定。如果我以您的示例为例,在调用 FindNext_Click 之前设置 SearchBox.Text = "document",则文本的位置准确。
  • @PaulH 这不是问题。我知道问题是什么。问题就在那里。
  • 我认为您需要向我们提供更多信息,我用粗体文本测试了您的代码,它工作正常。
  • @Bizz 正文之前有没有非文字元素?这就是破坏它的原因。

标签: c# wpf richtextbox


【解决方案1】:

不幸的是,TextRange.Text 去除了非文本字符,因此在这种情况下,IndexOf 计算的偏移量会稍微太低。这是主要问题。

我试图解决您的问题,并找到了有效的解决方案,即使我们在许多段落中设置了文本格式。

很多帮助都来自这个CodeProject Article。所以也请阅读那篇文章。

int curSearchLocation;
private void FindNext_Click(object sender, RoutedEventArgs e)
{
    TextRange text = new TextRange(RichEditor.Document.ContentStart, RichEditor.Document.ContentEnd);
    var location = text.Text.IndexOf(SearchBox.Text, curSearchLocation, StringComparison.CurrentCultureIgnoreCase);
    if (location < 0)
    {
        location = text.Text.IndexOf(SearchBox.Text, StringComparison.CurrentCultureIgnoreCase);
    }
    if (location >= 0)
    {
        curSearchLocation = location + 1;
        Select(location, SearchBox.Text.Length);
    }
    else
    {
        curSearchLocation = 0;
        MessageBox.Show("Not found");
    }
    RichEditor.Focus();                                   
}        

public void Select(int start, int length)
{
    TextPointer tp = RichEditor.Document.ContentStart;

    TextPointer tpLeft = GetPositionAtOffset(tp, start, LogicalDirection.Forward);
    TextPointer tpRight = GetPositionAtOffset(tp, start + length, LogicalDirection.Forward);
    RichEditor.Selection.Select(tpLeft, tpRight);            
}

private TextPointer GetPositionAtOffset(TextPointer startingPoint, int offset, LogicalDirection direction)
{
    TextPointer binarySearchPoint1 = null;
    TextPointer binarySearchPoint2 = null;

    // setup arguments appropriately
    if (direction == LogicalDirection.Forward)
    {
        binarySearchPoint2 = this.RichEditor.Document.ContentEnd;

        if (offset < 0)
        {
            offset = Math.Abs(offset);
        }
    }

    if (direction == LogicalDirection.Backward)
    {
        binarySearchPoint2 = this.RichEditor.Document.ContentStart;

        if (offset > 0)
        {
            offset = -offset;
        }
    }

    // setup for binary search
    bool isFound = false;
    TextPointer resultTextPointer = null;

    int offset2 = Math.Abs(GetOffsetInTextLength(startingPoint, binarySearchPoint2));
    int halfOffset = direction == LogicalDirection.Backward ? -(offset2 / 2) : offset2 / 2;

    binarySearchPoint1 = startingPoint.GetPositionAtOffset(halfOffset, direction);
    int offset1 = Math.Abs(GetOffsetInTextLength(startingPoint, binarySearchPoint1));

    // binary search loop

    while (isFound == false)
    {
        if (Math.Abs(offset1) == Math.Abs(offset))
        {
            isFound = true;
            resultTextPointer = binarySearchPoint1;
        }
        else
            if (Math.Abs(offset2) == Math.Abs(offset))
            {
                isFound = true;
                resultTextPointer = binarySearchPoint2;
            }
            else
            {
                if (Math.Abs(offset) < Math.Abs(offset1))
                {
                    // this is simple case when we search in the 1st half
                    binarySearchPoint2 = binarySearchPoint1;
                    offset2 = offset1;

                    halfOffset = direction == LogicalDirection.Backward ? -(offset2 / 2) : offset2 / 2;

                    binarySearchPoint1 = startingPoint.GetPositionAtOffset(halfOffset, direction);
                    offset1 = Math.Abs(GetOffsetInTextLength(startingPoint, binarySearchPoint1));
                }
                else
                {
                    // this is more complex case when we search in the 2nd half
                    int rtfOffset1 = startingPoint.GetOffsetToPosition(binarySearchPoint1);
                    int rtfOffset2 = startingPoint.GetOffsetToPosition(binarySearchPoint2);
                    int rtfOffsetMiddle = (Math.Abs(rtfOffset1) + Math.Abs(rtfOffset2)) / 2;
                    if (direction == LogicalDirection.Backward)
                    {
                        rtfOffsetMiddle = -rtfOffsetMiddle;
                    }

                    TextPointer binarySearchPointMiddle = startingPoint.GetPositionAtOffset(rtfOffsetMiddle, direction);
                    int offsetMiddle = GetOffsetInTextLength(startingPoint, binarySearchPointMiddle);

                    // two cases possible
                    if (Math.Abs(offset) < Math.Abs(offsetMiddle))
                    {
                        // 3rd quarter of search domain
                        binarySearchPoint2 = binarySearchPointMiddle;
                        offset2 = offsetMiddle;
                    }
                    else
                    {
                        // 4th quarter of the search domain
                        binarySearchPoint1 = binarySearchPointMiddle;
                        offset1 = offsetMiddle;
                    }
                }
            }
    }

    return resultTextPointer;
}

int GetOffsetInTextLength(TextPointer pointer1, TextPointer pointer2)
{
    if (pointer1 == null || pointer2 == null)
        return 0;

    TextRange tr = new TextRange(pointer1, pointer2);

    return tr.Text.Length;
}

希望此代码适用于您的情况。

【讨论】:

    猜你喜欢
    • 2016-05-19
    • 2011-01-26
    • 1970-01-01
    • 2013-01-10
    • 2018-04-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多