C#WPF为RichTextBox文本中的特定单词着色

问题描述 投票:0回答:2

我对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 *>的单词。它找到所有单词,但仍然有格式化字符的问题。

This is the result. The second word in the line has wrong coloring position

This is how the line looks like, when it search for the word

This is another trying

我看到了问题,当我添加color属性时,它会移动数据,但我的匹配包含着色前的位置。

看起来很简单,如果我在一行中有多个匹配,我总是按常数值移动位置。但格式化字符看起来并不总是相同的长度。正如您在第二次尝试中看到的那样,第一个变量颜色是正确的。比第二个有5个字符移位,第三个变量也有5个字符移位,第四个变量有9个字符移位,第五个变量有13个字符移位,第六个... ...(我不知道这里发生了什么) ,最后的第七个变量也具有良好的色彩位置。

wpf richtextbox
2个回答
1
投票

我发现了问题和解决方案。

问题:当正则表达式找到一行中的所有匹配项时,没有颜色格式。但是当我将颜色格式添加到第一个匹配时,它会移动文本,但正则表达式匹配结果仍然是旧位置。

解决方案:始终只更改第一场比赛。完成后,获取新的文本指针,将在彩色单词之前返回文本。然后再给你一个彩色的单词(它必须跳过,因为两次着色)。而不是在你的彩色单词之后给出文本,包含该行中的其他特殊单词。

示例:我想将这些单词着色为:<* word *>。

要着色的文字:“这是<* test *> <* sentence *>。”

  • 第一步:回馈整条生产线。正则表达式给出2个匹配(<* test *>,<* sentence *>)。着色第一个。
  • 第二步:回馈:“这是一个”。什么都不做
  • 第三步:回复:“<* test *>”。正则表达式给出1个匹配(<* test *>)。跳过它,因为它已经着色了。
  • 第四步:回馈:“”。什么都不做。
  • 第五步:回复:“<* sentence *>”。正则表达式给出1个匹配(<* sentence *>)。彩色化。
  • 第六步:回复:“<* sentence *>”。正则表达式给出1个匹配(<* sentence *>)。跳过它,因为它已经着色了。
  • 结束 { 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
投票

我并不是说这是最迷人的方式,但RichTextBox控件在标准的WPF工具包中并不是很容易使用。所以这是我做过你之前想要完成的事情的方式。

本质上,这个拆分会获取您的原始内容,将其拆分为流文档元素,然后将文档中的每个单词作为文本范围进行迭代。然后,如果符合条件,则将格式应用于每个单词,如Foreach中所述。希望这可以帮助。

P.S在考虑之后不是所有代码都可能需要,因为我的实现也有跳转到行功能,因此为什么我将文档拆分成行。祝好运!

     //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;      
  }

使用这种方法Highlighting keywords in a richtextbox in 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);
     }
  }
© www.soinside.com 2019 - 2024. All rights reserved.