WPF RichTextBox 语法高亮问题
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/17971300/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
WPF RichTextBox Syntax Highlighting Issue
提问by a7madx7
Hello everyone I've been working on a WPF application that has a text editor this text editor should apply some styling or colorizing over some tokens (keywords) to highlight it and make it obvious,,, the problem is i tried very very hard but i still get the same result which is when the user enters one of the keywords the whole text after that keyword is being styled ! just imagine if you types the " string " keyword in " C# " the whole text after it will be colored blue.
大家好,我一直在开发具有文本编辑器的 WPF 应用程序,该文本编辑器应该对某些标记(关键字)应用一些样式或着色以突出显示并使其明显,问题是我非常努力但是我仍然得到相同的结果,即当用户输入关键字之一时,该关键字被样式化后的整个文本!试想一下,如果您在“ C# ”中键入“ string ”关键字,则其后的整个文本将变为蓝色。
this was the code i used:
这是我使用的代码:
static List<string> tags = new List<string>(); static List<char> specials = new List<char>(); static string text; #region ctor static MainWindow() { string[] specialWords = { "string", "char", "null" }; tags = new List<string>(specialWords); // We also want to know all possible delimiters so adding this stuff. char[] chrs = { '.', ')', '(', '[', ']', '>', '<', ':', ';', '\n', '\t', '\r' }; specials = new List<char>(chrs); } public MainWindow() { InitializeComponent(); } #endregion //Now I should check statically if the string I passed is legal and constants in my dictionary public static bool IsKnownTag(string tag) { return tags.Exists(delegate(string s) { return s.ToLower().Equals(tag.ToLower()); }); } private static bool GetSpecials(char i) { foreach (var item in specials) { if (item.Equals(i)) { return true; } } return false; } // Wow. Great. Now I should separate words, that equals to my tags. For this propose we'll create new internal structure named Tag. This will help us to save words and its' positions. new struct Tag { public TextPointer StartPosition; public TextPointer EndPosition; public string Word; } internal void CheckWordsInRun(Run theRun){ //How, let's go through our text and save all tags we have to save. int sIndex = 0; int eIndex = 0; List<Tag> m_tags = new List<Tag>(); for (int i = 0; i < text.Length; i++) { if (Char.IsWhiteSpace(text[i]) | GetSpecials(text[i])) { if (i > 0 && !(Char.IsWhiteSpace(text[i - 1]) | GetSpecials(text[i - 1]))) { eIndex = i - 1; string word = text.Substring(sIndex, eIndex - sIndex + 1); if (IsKnownTag(word)) { Tag t = new Tag(); t.StartPosition = theRun.ContentStart.GetPositionAtOffset(sIndex, LogicalDirection.Forward); t.EndPosition = theRun.ContentStart.GetPositionAtOffset(eIndex + 1, LogicalDirection.Backward); t.Word = word; m_tags.Add(t); } } sIndex = i + 1; } } //How this works. But wait. If the word is last word in my text I'll never hightlight it, due I'm looking for separators. Let's add some fix for this case string lastWord = text.Substring(sIndex, text.Length - sIndex); if (IsKnownTag(lastWord)) { Tag t = new Tag(); t.StartPosition = theRun.ContentStart.GetPositionAtOffset(sIndex, LogicalDirection.Forward); t.EndPosition = theRun.ContentStart.GetPositionAtOffset(eIndex + 1, LogicalDirection.Backward); t.Word = lastWord; m_tags.Add(t); } //How I have all my words and its' positions in list. Let's color it! Dont forget to unsubscribe! text styling fires TextChanged event. txtStatus.TextChanged -= txtStatus_TextChanged; for (int i = 0; i < m_tags.Count; i++) { try { TextRange range = new TextRange(m_tags[i].StartPosition, m_tags[i].EndPosition); range.ApplyPropertyValue(TextElement.ForegroundProperty, new SolidColorBrush(Colors.Blue)); range.ApplyPropertyValue(TextElement.FontWeightProperty, FontWeights.Bold); } catch { } } m_tags.Clear(); txtStatus.TextChanged += txtStatus_TextChanged; }
static List<string> tags = new List<string>(); static List<char> specials = new List<char>(); static string text; #region ctor static MainWindow() { string[] specialWords = { "string", "char", "null" }; tags = new List<string>(specialWords); // We also want to know all possible delimiters so adding this stuff. char[] chrs = { '.', ')', '(', '[', ']', '>', '<', ':', ';', '\n', '\t', '\r' }; specials = new List<char>(chrs); } public MainWindow() { InitializeComponent(); } #endregion //Now I should check statically if the string I passed is legal and constants in my dictionary public static bool IsKnownTag(string tag) { return tags.Exists(delegate(string s) { return s.ToLower().Equals(tag.ToLower()); }); } private static bool GetSpecials(char i) { foreach (var item in specials) { if (item.Equals(i)) { return true; } } return false; } // Wow. Great. Now I should separate words, that equals to my tags. For this propose we'll create new internal structure named Tag. This will help us to save words and its' positions. new struct Tag { public TextPointer StartPosition; public TextPointer EndPosition; public string Word; } internal void CheckWordsInRun(Run theRun){ //How, let's go through our text and save all tags we have to save. int sIndex = 0; int eIndex = 0; List<Tag> m_tags = new List<Tag>(); for (int i = 0; i < text.Length; i++) { if (Char.IsWhiteSpace(text[i]) | GetSpecials(text[i])) { if (i > 0 && !(Char.IsWhiteSpace(text[i - 1]) | GetSpecials(text[i - 1]))) { eIndex = i - 1; string word = text.Substring(sIndex, eIndex - sIndex + 1); if (IsKnownTag(word)) { Tag t = new Tag(); t.StartPosition = theRun.ContentStart.GetPositionAtOffset(sIndex, LogicalDirection.Forward); t.EndPosition = theRun.ContentStart.GetPositionAtOffset(eIndex + 1, LogicalDirection.Backward); t.Word = word; m_tags.Add(t); } } sIndex = i + 1; } } //How this works. But wait. If the word is last word in my text I'll never hightlight it, due I'm looking for separators. Let's add some fix for this case string lastWord = text.Substring(sIndex, text.Length - sIndex); if (IsKnownTag(lastWord)) { Tag t = new Tag(); t.StartPosition = theRun.ContentStart.GetPositionAtOffset(sIndex, LogicalDirection.Forward); t.EndPosition = theRun.ContentStart.GetPositionAtOffset(eIndex + 1, LogicalDirection.Backward); t.Word = lastWord; m_tags.Add(t); } //How I have all my words and its' positions in list. Let's color it! Dont forget to unsubscribe! text styling fires TextChanged event. txtStatus.TextChanged -= txtStatus_TextChanged; for (int i = 0; i < m_tags.Count; i++) { try { TextRange range = new TextRange(m_tags[i].StartPosition, m_tags[i].EndPosition); range.ApplyPropertyValue(TextElement.ForegroundProperty, new SolidColorBrush(Colors.Blue)); range.ApplyPropertyValue(TextElement.FontWeightProperty, FontWeights.Bold); } catch { } } m_tags.Clear(); txtStatus.TextChanged += txtStatus_TextChanged; }
and here is the text changed event handler
这是文本更改事件处理程序
private void txtStatus_TextChanged(object sender, TextChangedEventArgs e) { if (txtStatus.Document == null) return; TextRange documentRange = new TextRange(txtStatus.Document.ContentStart, txtStatus.Document.ContentEnd); //documentRange.ClearAllProperties(); text = documentRange.Text; //Now let's create navigator to go though the text and hightlight it TextPointer navigator = txtStatus.Document.ContentStart; while (navigator.CompareTo(txtStatus.Document.ContentEnd) < 0) { TextPointerContext context = navigator.GetPointerContext(LogicalDirection.Backward); if (context == TextPointerContext.ElementStart && navigator.Parent is Run) { CheckWordsInRun((Run)navigator.Parent); } navigator = navigator.GetNextContextPosition(LogicalDirection.Forward); } }
private void txtStatus_TextChanged(object sender, TextChangedEventArgs e) { if (txtStatus.Document == null) return; TextRange documentRange = new TextRange(txtStatus.Document.ContentStart, txtStatus.Document.ContentEnd); //documentRange.ClearAllProperties(); text = documentRange.Text; //Now let's create navigator to go though the text and hightlight it TextPointer navigator = txtStatus.Document.ContentStart; while (navigator.CompareTo(txtStatus.Document.ContentEnd) < 0) { TextPointerContext context = navigator.GetPointerContext(LogicalDirection.Backward); if (context == TextPointerContext.ElementStart && navigator.Parent is Run) { CheckWordsInRun((Run)navigator.Parent); } navigator = navigator.GetNextContextPosition(LogicalDirection.Forward); } }
Any advice or hand will be greatly appreciated,, thanks in advance.
任何建议或手将不胜感激,提前致谢。
回答by kennyzx
You should highlight the keywords until all the text is parsed, highlighting the keywords in each Runwill affect the call to navigator.GetNextContextPosition, causing unexpected errors like firing textchanged event repeatedly.
你应该高亮关键字,直到所有的文本都被解析,高亮每个中的关键字Run会影响对 的调用navigator.GetNextContextPosition,导致意外错误,如重复触发 textchanged 事件。
And after you hightlight a keyword, the text you type after that keyword INHERITS the style of that keyword. One workaround is calling ClearAllPropertieson the whole text before you hight light the keywords.
在您突出显示某个关键字后,您在该关键字后键入的文本会继承该关键字的样式。一种解决方法是ClearAllProperties在突出显示关键字之前调用整个文本。
Below is the updated txtStatus_TextChangedand CheckWordsInRunmethod.
以下是更新txtStatus_TextChanged和CheckWordsInRun方法。
List<Tag> m_tags = new List<Tag>();
internal void CheckWordsInRun(Run theRun) //do not hightlight keywords in this method
{
//How, let's go through our text and save all tags we have to save.
int sIndex = 0;
int eIndex = 0;
for (int i = 0; i < text.Length; i++)
{
if (Char.IsWhiteSpace(text[i]) | GetSpecials(text[i]))
{
if (i > 0 && !(Char.IsWhiteSpace(text[i - 1]) | GetSpecials(text[i - 1])))
{
eIndex = i - 1;
string word = text.Substring(sIndex, eIndex - sIndex + 1);
if (IsKnownTag(word))
{
Tag t = new Tag();
t.StartPosition = theRun.ContentStart.GetPositionAtOffset(sIndex, LogicalDirection.Forward);
t.EndPosition = theRun.ContentStart.GetPositionAtOffset(eIndex + 1, LogicalDirection.Backward);
t.Word = word;
m_tags.Add(t);
}
}
sIndex = i + 1;
}
}
//How this works. But wait. If the word is last word in my text I'll never hightlight it, due I'm looking for separators. Let's add some fix for this case
string lastWord = text.Substring(sIndex, text.Length - sIndex);
if (IsKnownTag(lastWord))
{
Tag t = new Tag();
t.StartPosition = theRun.ContentStart.GetPositionAtOffset(sIndex, LogicalDirection.Forward);
t.EndPosition = theRun.ContentStart.GetPositionAtOffset(text.Length, LogicalDirection.Backward); //fix 1
t.Word = lastWord;
m_tags.Add(t);
}
}
private void txtStatus_TextChanged(object sender, TextChangedEventArgs e)
{
if (txtStatus.Document == null)
return;
txtStatus.TextChanged -= txtStatus_TextChanged;
m_tags.Clear();
//first clear all the formats
TextRange documentRange = new TextRange(txtStatus.Document.ContentStart, txtStatus.Document.ContentEnd);
documentRange.ClearAllProperties();
//text = documentRange.Text; //fix 2
//Now let's create navigator to go though the text, find all the keywords but do not hightlight
TextPointer navigator = txtStatus.Document.ContentStart;
while (navigator.CompareTo(txtStatus.Document.ContentEnd) < 0)
{
TextPointerContext context = navigator.GetPointerContext(LogicalDirection.Backward);
if (context == TextPointerContext.ElementStart && navigator.Parent is Run)
{
text = ((Run)navigator.Parent).Text; //fix 2
if (text != "")
CheckWordsInRun((Run)navigator.Parent);
}
navigator = navigator.GetNextContextPosition(LogicalDirection.Forward);
}
//only after all keywords are found, then we highlight them
for (int i = 0; i < m_tags.Count; i++)
{
try
{
TextRange range = new TextRange(m_tags[i].StartPosition, m_tags[i].EndPosition);
range.ApplyPropertyValue(TextElement.ForegroundProperty, new SolidColorBrush(Colors.Blue));
range.ApplyPropertyValue(TextElement.FontWeightProperty, FontWeights.Bold);
}
catch { }
}
txtStatus.TextChanged += txtStatus_TextChanged;
}

