我对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
我看到了问题,当我添加颜色属性时,它会移动数据,但我的匹配包含着色之前的位置。
这看起来很简单,如果我在一行中有多个匹配项,我总是按恒定值移动位置。但格式化字符看起来并不总是相同的长度。正如您在第二次尝试中看到的,第一个可变颜色是正确的。第二个变量有 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/