我对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
我看到了问题,当我添加color属性时,它会移动数据,但我的匹配包含着色前的位置。
看起来很简单,如果我在一行中有多个匹配,我总是按常数值移动位置。但格式化字符看起来并不总是相同的长度。正如您在第二次尝试中看到的那样,第一个变量颜色是正确的。比第二个有5个字符移位,第三个变量也有5个字符移位,第四个变量有9个字符移位,第五个变量有13个字符移位,第六个... ...(我不知道这里发生了什么) ,最后的第七个变量也具有良好的色彩位置。
我发现了问题和解决方案。
问题:当正则表达式找到一行中的所有匹配项时,没有颜色格式。但是当我将颜色格式添加到第一个匹配时,它会移动文本,但正则表达式匹配结果仍然是旧位置。
解决方案:始终只更改第一场比赛。完成后,获取新的文本指针,将在彩色单词之前返回文本。然后再给你一个彩色的单词(它必须跳过,因为两次着色)。而不是在你的彩色单词之后给出文本,包含该行中的其他特殊单词。
示例:我想将这些单词着色为:<* word *>。
要着色的文字:“这是<* test *> <* 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);
}
}
我并不是说这是最迷人的方式,但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);
}
}