Выбор диапазона текста в WPF RichTextBox (FlowDocument) программным способом - PullRequest
17 голосов
/ 21 сентября 2009

У меня есть этот WPF RichTextBox, и я хочу программно выбрать заданный диапазон букв / слов и выделить его. Я пробовал это, но это не работает, вероятно, потому что я не принимаю во внимание некоторые скрытые теги FlowDocument или подобные. Например, я хочу выбрать буквы 3-8, но выбираются 2-6):

var start = MyRichTextBox.Document.ContentStart;
var startPos = start.GetPositionAtOffset(3);
var endPos = start.GetPositionAtOffset(8);
var textRange = new TextRange(startPos,endPos);
textRange.ApplyPropertyValue(TextElement.ForegroundProperty,
    new SolidColorBrush(Colors.Blue));
textRange.ApplyPropertyValue(TextElement.FontWeightProperty, 
    FontWeights.Bold);

Я понял, что обработка RichTextBox немного сложнее, чем я думал:)

Обновление: я получил несколько ответов на форумах MSDN: Эта тема , где seid "dekurver":

Указанные смещения не являются смещения символов, но смещения символов. Что вам нужно сделать, это получить TextPointer, который вы знаете, находится рядом к тексту, тогда вы можете добавить символ Смещения.

И «ЛестерЛобо» сказал:

вам нужно будет пройти через абзацы и строки, чтобы найти Далее, а затем их смещения в цикле подать заявку на все выступления конкретный текст. обратите внимание, что при редактировании ваш текст будет двигаться, но ваш выделить не будет двигаться как его связано со смещением, а не текст. Однако вы можете создать пользовательский запуск и выделить это ...

Все равно ЛЮБЛЮ видеть какой-то пример кода для этого, если кто-то знает, как обходить FlowDocuments ...

РЕДАКТИРОВАТЬ Я получил работающую версию кода Kratz VB, она выглядит так:

private static TextPointer GetPoint(TextPointer start, int x)
{
    var ret = start;
    var i = 0;
    while (i < x && ret != null)
    {
        if (ret.GetPointerContext(LogicalDirection.Backward) == 
TextPointerContext.Text ||
            ret.GetPointerContext(LogicalDirection.Backward) == 
TextPointerContext.None)
            i++;
        if (ret.GetPositionAtOffset(1, 
LogicalDirection.Forward) == null)
            return ret;
        ret = ret.GetPositionAtOffset(1, 
LogicalDirection.Forward);
    }
    return ret;
}

И я использую это так:

Colorize(item.Offset, item.Text.Length, Colors.Blue);

private void Colorize(int offset, int length, Color color)
{
    var textRange = MyRichTextBox.Selection;
    var start = MyRichTextBox.Document.ContentStart;
    var startPos = GetPoint(start, offset);
    var endPos = GetPoint(start, offset + length);

    textRange.Select(startPos, endPos);
    textRange.ApplyPropertyValue(TextElement.ForegroundProperty, 
new SolidColorBrush(color));
    textRange.ApplyPropertyValue(TextElement.FontWeightProperty, 
FontWeights.Bold);
}

Ответы [ 8 ]

11 голосов
/ 23 сентября 2009
Public Function GoToPoint(ByVal start As TextPointer, ByVal x As Integer) As TextPointer
    Dim out As TextPointer = start
    Dim i As Integer = 0
    Do While i < x
        If out.GetPointerContext(LogicalDirection.Backward) = TextPointerContext.Text Or _
             out.GetPointerContext(LogicalDirection.Backward) = TextPointerContext.None Then
            i += 1
        End If
        If out.GetPositionAtOffset(1, LogicalDirection.Forward) Is Nothing Then
            Return out
        Else
            out = out.GetPositionAtOffset(1, LogicalDirection.Forward)
        End If


    Loop
    Return out
End Function

Попробуйте, это должно вернуть текстовый указатель для данного смещения символа. (Извините, это в VB, но это то, над чем я работаю ...)

8 голосов
/ 21 сентября 2009

Попробуйте это:

var textRange = MyRichTextBox.Selection;
var start = MyRichTextBox.Document.ContentStart;
var startPos = start.GetPositionAtOffset(3);
var endPos = start.GetPositionAtOffset(8);
textRange.Select(startPos, endPos);
textRange.ApplyPropertyValue(TextElement.ForegroundProperty, new SolidColorBrush(Colors.Blue));
textRange.ApplyPropertyValue(TextElement.FontWeightProperty, FontWeights.Bold);
7 голосов
/ 13 сентября 2012

Я попытался использовать решение, опубликованное KratzVB, но обнаружил, что оно игнорирует переводы строки. Если вы хотите считать символы \ r и \ n, тогда этот код должен работать:

private static TextPointer GetPoint(TextPointer start, int x)
{

        var ret = start;
        var i = 0;
        while (ret != null)
        {
            string stringSoFar = new TextRange(ret, ret.GetPositionAtOffset(i, LogicalDirection.Forward)).Text;
            if (stringSoFar.Length == x)
                    break;
            i++;
            if (ret.GetPositionAtOffset(i, LogicalDirection.Forward) == null)
                return ret.GetPositionAtOffset(i-1, LogicalDirection.Forward)

        }
        ret=ret.GetPositionAtOffset(i, LogicalDirection.Forward);
        return ret;
}
2 голосов
/ 14 ноября 2014

Не удалось найти решение с приемлемым по производительности решением этой проблемы в течение длительного времени. Следующий пример работает в моем случае с максимальной производительностью. Надеюсь, это кому-нибудь тоже поможет.

TextPointer startPos = rtb.Document.ContentStart.GetPositionAtOffset(searchWordIndex, LogicalDirection.Forward);
startPos = startPos.CorrectPosition(searchWord, FindDialog.IsCaseSensitive);
if (startPos != null)
{
    TextPointer endPos = startPos.GetPositionAtOffset(textLength, LogicalDirection.Forward);
    if (endPos != null)
    {
         rtb.Selection.Select(startPos, endPos);
    }
}

public static TextPointer CorrectPosition(this TextPointer position, string word, bool caseSensitive)
{
   TextPointer start = null;
   while (position != null)
   {
       if (position.GetPointerContext(LogicalDirection.Forward) == TextPointerContext.Text)
       {
           string textRun = position.GetTextInRun(LogicalDirection.Forward);

           int indexInRun = textRun.IndexOf(word, caseSensitive ? StringComparison.InvariantCulture : StringComparison.InvariantCultureIgnoreCase);
           if (indexInRun >= 0)
           {
               start = position.GetPositionAtOffset(indexInRun);
               break;
           }
       }

       position = position.GetNextContextPosition(LogicalDirection.Forward);
   }

   return start; 
}
2 голосов
/ 01 октября 2013

Моя версия основана на версии cave_dweller

private static TextPointer GetPositionAtCharOffset(TextPointer start, int numbertOfChars)
{
    var offset = start;
    int i = 0;
    string stringSoFar="";
    while (stringSoFar.Length < numbertOfChars)
    {
        i++;
        TextPointer offsetCandidate = start.GetPositionAtOffset(
                i, LogicalDirection.Forward);

        if (offsetCandidate == null)
            return offset; // ups.. we are to far

        offset = offsetCandidate;
        stringSoFar = new TextRange(start, offset).Text;
    }

    return offset;
}

Чтобы пропустить некоторые символы, добавьте этот код в цикл:

stringSoFar = stringSoFar.Replace("\r\n", "")
                         .Replace(" ", "")

Вместо этого (медленно):

var startPos = GetPoint(start, offset);
var endPos = GetPoint(start, offset + length);

Вы должны сделать это (быстрее)

var startPos = GetPoint(start, offset);
var endPos = GetPoint(startPos, length);

Или создайте отдельный метод для получения TextRange:

private static TextRange GetTextRange(TextPointer start, int startIndex, int length)
{
    var rangeStart = GetPositionAtCharOffset(start, startIndex);
    var rangeEnd = GetPositionAtCharOffset(rangeStart, length);
    return new TextRange(rangeStart, rangeEnd);
}

Теперь вы можете форматировать текст без Select() ing:

var range = GetTextRange(Document.ContentStart, 3, 8);
range.ApplyPropertyValue(
    TextElement.BackgroundProperty, 
    new SolidColorBrush(Colors.Aquamarine));
1 голос
/ 12 августа 2010

Между прочим (и это может быть академическим для всех, кроме меня), если вы установите FocusManager.IsFocusScope = "True" для контейнера RichTextBox, например Grid,

<Grid FocusManager.IsFocusScope="True">...</Grid>

, тогда вы сможете использовать метод Colorize Йохана Данфорта без двух вызовов ApplyPropertyValue, а RichTextBox должен использовать выбранный по умолчанию фон и передний план, чтобы выделить выделение.

private void Colorize(int offset, int length, Color color)
{
    var textRange = MyRichTextBox.Selection;
    var start = MyRichTextBox.Document.ContentStart;
    var startPos = GetPoint(start, offset);
    var endPos = GetPoint(start, offset + length);

    textRange.Select(startPos, endPos);
}

Не пробовал его с RichTextBox, но он работает довольно хорошо при шаблонировании поиска TextBox в FlowDocumentReader. Просто чтобы быть уверенным, что вы также можете установить

<RichTextBox FocusManager.FocusedElement="{Binding RelativeSource={RelativeSource Self}}">...</RichTextBox>

, чтобы обеспечить фокусировку RichTextBox в своей области фокусировки.

Недостатком этого, конечно, является то, что если пользователь щелкает или выполняет выбор в RichTextBox, ваш выбор исчезает.

0 голосов
/ 22 февраля 2019
    private void SelectText(int start, int length)
    {
        TextRange textRange = new TextRange(richTextBox.Document.ContentStart, richTextBox.Document.ContentEnd);
        TextPointer pointerStart = textRange.Start.GetPositionAtOffset(start, LogicalDirection.Forward);
        TextPointer pointerEnd = textRange.Start.GetPositionAtOffset(start + length, LogicalDirection.Backward);

        richTextBox.Selection.Select(pointerStart, pointerEnd);
    }
0 голосов
/ 11 февраля 2016
    private TextPointer GetPoint(TextPointer start, int pos)
    {
        var ret = start;
        int i = 0;
        while (i < pos)
        {
            if (ret.GetPointerContext(LogicalDirection.Forward) ==
    TextPointerContext.Text)
                i++;
            if (ret.GetPositionAtOffset(1, LogicalDirection.Forward) == null)
                return ret;
            ret = ret.GetPositionAtOffset(1, LogicalDirection.Forward);
        }
        return ret;
    }
...