wpf - C# WPF 对 RichTextBox 文本中的特定单词进行着色

标签 wpf richtextbox

我对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

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

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

最佳答案

我也找到了问题和解决方案。

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

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

示例: 我想对单词进行着色,如下所示:<*word*>。

要着色的文本:“这是一个<*测试*><*句子*>。”

  • 第一步:返回整行。正则表达式给出 2 个匹配项 (<*测试*>,<*句子*>)。为第一个着色。

  • 第二步:返回:“这是一个”。对此什么也不做

  • 第三步:返回:“<*test*>”。正则表达式给出 1 个匹配项 (<*test*>).Skip 它,因为它已经着色了。
  • 第四步:返回:“”。对此什么也不做。
  • 第五步:返回:“<*sentence*>”。正则表达式给出 1 个匹配项 (<*句子*>)。着色。
  • 第六步:返回:“<*sentence*>”。正则表达式给出 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);
        }
    }
    

关于wpf - C# WPF 对 RichTextBox 文本中的特定单词进行着色,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54810368/

相关文章:

c# - WPF 使列滑入位置 XAML

c# - 适用于 Windows 剪贴板的 C# 代码?

c# - 如何使用 Resources.resx 链接图像

C# FlowDocument RichTextBox 对齐不持久

c# - 将新段落追加到 RichTextBox

WPF RichTextBox TextChanged 事件 - 如何查找已删除或插入的文本?

仅带颜色的 WPF 文本控件

c# - wpf 附加属性 : where to unsubscribe from event handling?

wpf - 从不同的 View 执行相同的命令 - MMVM

c# - 正则表达式在包含单词的行的开头拆分