RichTextBox在有趣的地方找到我的搜索词

时间:2014-01-24 13:44:10

标签: c# wpf richtextbox

我正在尝试在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();
}

当我搜索“文档”时会发生这种情况:

Screenshot

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

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

1 个答案:

答案 0 :(得分:0)

不幸的是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;
}

希望这段代码适合您的情况。