C # WPF раскрасить определенные слова в тексте RichTextBox - PullRequest
0 голосов
/ 21 февраля 2019

У меня неправильное представление о FlowDocument, пожалуйста, помогите мне разобраться.Я работаю над редактором исходного кода, где пользователь может добавить несколько специальных переменных, а затем программа ищет эти переменные.Для этого редактора я использую RichTextBox (RTB).Я хотел бы использовать цвет для этих переменных.Это не было проблемой, чтобы добавить цвет, когда пользователь добавляет новую переменную в текст.Но когда пользователь сначала открывает исходный код, в котором уже есть некоторые переменные, мне нужно пройтись по всему тексту и раскрасить переменные.


Код ниже: сначала я ищу все переменные и их положение с помощью регулярных выражений. (Переменные выглядят так: <* variable *>) Затем циклично проходит и меняет цвет один за другим, но когдаЯ делаю TextRange, GetPositionAtOffset возвращает неправильное значение.Я знаю, что это из-за специальных символов форматирования, которые также считаются с помощью GetPositionAtOffset.Вопрос в том, как я могу решить эту проблему?

private void ColorizeAllVariable(TextRange TR_Input)
    {
        Regex regex = new Regex(@"(<\*.[^<\*>]*\*>)");
        MatchCollection matches = regex.Matches(TR_Input.Text);
        NoRTBChangeEvent = true;
        for (int i = 0; i < matches.Count; i++)
        {
            TextRange TR_Temp = new TextRange(TR_Input.Start.GetPositionAtOffset(matches[i].Index), TR_Input.Start.GetPositionAtOffset(matches[i].Index + matches[i].Length));
            TR_Temp.ApplyPropertyValue(TextElement.ForegroundProperty, Brushes.DodgerBlue);
        }
        NoRTBChangeEvent = false;
    }

Обновление 1:

После решения user8478480 я изменил свойкод.

private void ColorizeAllVariable(RichTextBox richTextBox)
    {
        IEnumerable<TextRange> WordRanges = GetAllWordRanges(richTextBox.Document, @"(<\*.[^<\*>]*\*>)");

        foreach (TextRange WordRange in WordRanges)
        {
            WordRange.ApplyPropertyValue(TextElement.ForegroundProperty, Brushes.DodgerBlue);
        }
    }

private static IEnumerable<TextRange> GetAllWordRanges(FlowDocument document, string pattern)
    {
        TextPointer pointer = document.ContentStart;
        while (pointer != null)
        {
            if (pointer.GetPointerContext(LogicalDirection.Forward) == TextPointerContext.Text)
            {
                string textRun = pointer.GetTextInRun(LogicalDirection.Forward);
                MatchCollection matches = Regex.Matches(textRun, pattern);
                foreach (Match match in matches)
                {
                    int startIndex = match.Index;
                    int length = match.Length;
                    TextPointer start = pointer.GetPositionAtOffset(startIndex);
                    TextPointer end = start.GetPositionAtOffset(length);
                    yield return new TextRange(start, end);
                }
            }
            pointer = pointer.GetNextContextPosition(LogicalDirection.Forward);
        }
    }

Он непосредственно ищет слова, которые выглядят как <* word *>.И он находит все слова, но все еще имеют проблемы с форматированием символов.

Это результат.Второе слово в строке имеет неправильную позицию окраски

Так выглядит строка при поиске слова

Этоеще одна попытка

Я вижу проблему, когда я добавляю свойство цвета, оно сдвигает данные, но мое совпадение содержит позицию до раскрашивания.

Это выглядит легко, если у меня есть большечем одно совпадение в одной строке я всегда смещаю позицию на постоянное значение.Но символы форматирования не всегда выглядят одинаково.Как вы можете видеть со второй попытки, цвет первой переменной правильный.Чем вторая имеет 5 символов, третья переменная также имеет 5 символов, четвертая переменная имеет 9 символов, пятая - 13 символов, шестая ... (я понятия не имею, что здесь происходит)и последняя седьмая переменная также имеет хорошую цветовую позицию.

Ответы [ 2 ]

0 голосов
/ 13 марта 2019

Я также нашел проблему и решение.

Проблема: Когда регулярное выражение находит все совпадения в строке, цветовое форматирование отсутствует.Но когда я добавляю формат цвета к первому совпадению, он сдвигает текст, но результат совпадения с регулярным выражением имеет прежние позиции.

Решение: Всегда изменяйте только первое совпадение.Когда это будет сделано, получите новый текстовый указатель, который вернет вам текст перед вашим цветным словом.Затем возвращает снова ваше цветное слово (оно должно пропустить, из-за двухкратного окрашивания).И затем возвращает текст после вашего цветного слова, что содержит другие специальные слова в строке.

Пример: Я хочу раскрасить слова так: <* word *>.

Текст для раскраски: "Это <* test *> <*предложение *>. "

  • Первый шаг: возвращает всю строку.Regex дает 2 совпадения (<* test *>, <* предложение *>).Раскрась первый.

  • Второй шаг: Возвращает: «Это».Ничего не делать с этим

  • Третий шаг: возвращает: "<* test *>".Regex дает 1 совпадение (<* test *>). Пропустите его, потому что оно уже раскрашено.
  • Четвертый шаг: возвращает: "".Ничего с этим не делать.
  • Пятый шаг: Возвращает: "<* предложение *>".Regex дает 1 совпадение (<* предложение *>).Раскрасить.
  • Шестой шаг: возвращает: "<* предложение *>".Regex дает 1 совпадение (<* предложение *>).Пропустите его, потому что он уже раскрашен.
  • Конец

    {
        TextPointer pointer = document.ContentStart;
        bool Skip = false;
        string textRun = "";
    
        while (pointer != null)
        {
            if (pointer.GetPointerContext(LogicalDirection.Forward) == TextPointerContext.Text)
            {
                do
                {
                    if (!Skip)
                    {
                        textRun = pointer.GetTextInRun(LogicalDirection.Forward);
                        MatchCollection Matches = Regex.Matches(textRun, pattern);
                        if (Matches.Count > 0)
                        {
                            Skip = true;
                            int startIndex = Matches[0].Index;
                            int length = Matches[0].Length;
                            TextPointer start = pointer.GetPositionAtOffset(startIndex);
                            TextPointer end = start.GetPositionAtOffset(length);
                            yield return new TextRange(start, end);
                        }
                    }
                    else
                    {
                        pointer = pointer.GetNextContextPosition(LogicalDirection.Forward);
                        if (pointer.GetPointerContext(LogicalDirection.Forward) == TextPointerContext.Text)
                        {
                            textRun = pointer.GetTextInRun(LogicalDirection.Forward);
                            if(Regex.IsMatch(textRun,pattern))
                            {
                                Skip = false;
                            }
                        }
                    }
                } while (Skip);
            }
            pointer = pointer.GetNextContextPosition(LogicalDirection.Forward);
        }
    }
    
0 голосов
/ 21 февраля 2019

Я не говорю, что это самый эффектный способ, но элемент управления RichTextBox не очень прост в использовании в стандартном наборе инструментов WPF.Итак, вот способ, которым я сделал то, что вы пытаетесь выполнить раньше.

По сути, это разделение берет ваш исходный контент, разбивает его на элементы потока документа и затем перебирает каждое слово из документа в виде текста.спектр.Затем он применяет форматирование к каждому слову, если оно соответствует критериям, как указано в Foreach.Надеюсь, это поможет.

PS, подумав об этом, может понадобиться не весь код, так как моя реализация также имела функцию перехода на строку, поэтому я разбил документ на строки.Удачи!

     //new doc.
     var doc = new FlowDocument();

     //loop all lines from text.(split on \r\n)
     string[] lines = RichTextBoxExtraControl.Text.Split(new string[] { "\r\n" }, StringSplitOptions.None);
     for (int i = 0; i < lines.Length; i++)
     {

        //make new paragraph
        var run = new Run(lines[i]);
        var par = new Paragraph(run);
        par.LineHeight = 1;
        doc.Blocks.Add(par);
    }

     //Searches a list of all words to highlight in place the words below
     IEnumerable<TextRange> wordRanges = GetAllWordRanges(doc);
     foreach (TextRange wordRange in wordRanges)
     {
        if (wordRange.Text == ">WORD YOU WANT TO HIGHLIGHT<")
        {
           wordRange.ApplyPropertyValue(TextElement.BackgroundProperty, Brushes.Red); //Effect to apply.
        }
     }

     //Set document.
     RichTextBox1.Document = doc;      
  }

с использованием этого метода Подсветка ключевых слов в richtextbox в WPF

  public static IEnumerable<TextRange> GetAllWordRanges(FlowDocument document)
  {
     string pattern = @"[^\W\d](\w|[-']{1,2}(?=\w))*";
     TextPointer pointer = document.ContentStart;
     while (pointer != null)
     {
        if (pointer.GetPointerContext(LogicalDirection.Forward) == TextPointerContext.Text)
        {
           string textRun = pointer.GetTextInRun(LogicalDirection.Forward);
           MatchCollection matches = Regex.Matches(textRun, pattern);
           foreach (Match match in matches)
           {
              int startIndex = match.Index;
              int length = match.Length;
              TextPointer start = pointer.GetPositionAtOffset(startIndex);
              TextPointer end = start.GetPositionAtOffset(length);
              yield return new TextRange(start, end);
           }
        }
        pointer = pointer.GetNextContextPosition(LogicalDirection.Forward);
     }
  }
...