RichTextBox syntax highlighting
Today we’ll learn how to create RichTextBox with syntax highlight ing. Syntax highlighting is a feature of some text editors that displays text—especially source code—in different colors and fonts according to the category of terms. This feature eases writing in a structured language such as a programming language or a markup language as both structures and syntax errors are visually distinct.
The first thing we need is RichTextBox. So, do it. I did some trick on it in order to prevent from <P> (Paragraph) inserting on each enter (with is default behavior of RichTextBox). I want only one line down when I pressed my Enter key.
<RichTextBox ScrollViewer.CanContentScroll="True" ScrollViewer.VerticalScrollBarVisibility="Visible" Name="TextInput" AcceptsReturn="True" TextChanged="TextChangedEventHandler">
<RichTextBox.Resources>
<Style TargetType="{x:Type Paragraph}">
<Setter Property="Margin" Value="0"/>
</Style>
</RichTextBox.Resources>
</RichTextBox>
Ok, not I want only plaintext in my TextBox TextChanged event ‘cos I’m going to color it myself.
private void TextChangedEventHandler(object sender, TextChangedEventArgs e)
{
if (TextInput.Document == null)
return;
TextRange documentRange = new TextRange(TextInput.Document.ContentStart, TextInput.Document.ContentEnd);
documentRange.ClearAllProperties();
Now let’s create navigator to go though the text and hightlight it
TextPointer navigator = TextInput.Document.ContentStart;
while (navigator.CompareTo(TextInput.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);
}
So the only thing we have to do is to check each word within my navigator, compare it to Highlighting dictionary and color it. Huh, where is my dictionary? Here it comes. I’ll do it for ActionScript for my client.
class JSSyntaxProvider
{
static List<string> tags = new List<string>();
static List<char> specials = new List<char>();
#region ctor
static JSSyntaxProvider()
{
string[] strs = {
"Anchor",
"Applet",
"Area",
"Array",
"Boolean",.....tags = new List<string>(strs);
We also want to know all possible delimiters so adding this stuff.
char[] chrs = {
'.',
')',
'(',
'[',
']',
'>',
'<',
':',
';',
'\n',
'\t'
};
specials = new List<char>(chrs);
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()); });
}
Also, I’ll do kind of Intellisense later, so I want to check the beginnings of my tags as well
public static List<string> GetJSProvider(string tag)
{
return tags.FindAll(delegate(string s) { return s.ToLower().StartsWith(tag.ToLower()); });
}
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;
}
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]) | JSSyntaxProvider.GetSpecials.Contains(text[i]))
{
if (i > 0 && !(Char.IsWhiteSpace(text[i - 1]) | JSSyntaxProvider.GetSpecials.Contains(text[i - 1])))
{
eIndex = i - 1;
string word = text.Substring(sIndex, eIndex - sIndex + 1);
if (JSSyntaxProvider.IsKnownTag(word))
{
Tag t = new Tag();
t.StartPosition = run.ContentStart.GetPositionAtOffset(sIndex, LogicalDirection.Forward);
t.EndPosition = run.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 (JSSyntaxProvider.IsKnownTag(lastWord))
{
Tag t = new Tag();
t.StartPosition = run.ContentStart.GetPositionAtOffset(sIndex, LogicalDirection.Forward);
t.EndPosition = run.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.
TextInput.TextChanged -= this.TextChangedEventHandler;
for(int i=0;i<m_tags.Count;i++)
{
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);
}
m_tags.Clear();
TextInput.TextChanged += this.TextChangedEventHandler;
That’s all. We have nice RichTextBox, that understands and color any syntax we want. Maybe later we’ll add Intellisense for it.
You may also be interested with:
- INotifyPropertyChanged auto wiring or how to get rid of redundant code
- Real singleton approach in WPF application
December 13th, 2006 · Comments (18)
18 Responses to “RichTextBox syntax highlighting”
Leave a Reply
Discover other tags
My tools
- .NET Framework Detector
- Duplicate images finder
- Exchange Security Policy for Windows Mobile Devices Fix
- Gas Price Windows Vista SideBar gadget
- Israel Traffic Information Windows Vista SideBar gadget
- Localization fix for SAP ES Explorer for Visual Studio
- LocTester
- RTL and LTR in Windows Live Writer
- Silverlight controls library
- Snipping tool integration plugin for WLW
- USB FM receiver library
- Vista Battery Saver
- WebCam control for WPF
- Windows Live SkyDrive attachment for Windows Live Writer
- Wireless Migrator
- WPF Virtual Keyboard




January 1st, 2009 at 12:03 am
Very slow + A complete rip off from another source. Greate job anyway.
January 1st, 2009 at 12:03 am
Thank you very much.
January 1st, 2009 at 12:03 am
I agree with all who say this is slow – great tutorial, but the end result runs at a snail’s pace. Any clues?
January 1st, 2009 at 12:03 am
I won’t thank you, instead I’ll just *** and whine about how this doesn’t suit me. Why are you not pandering to my needs? I think I’ve waited long enough for the solution I need.
January 1st, 2009 at 12:03 am
most part of this sample was taken from Windows Vista SDK 9.0 in
WPF-Samples section. There in nothing new… this way of implementation (of Microsoft's developers) is very slow, dependly
from word cont in RichTextBox
((
i'll try to do something with it, but when anybody find a solution,
please let me know to "xeonix@i.ua".
…thanks
January 1st, 2009 at 12:03 am
sloooowwwww
January 1st, 2009 at 12:03 am
Your code works very slowly.
January 1st, 2009 at 12:03 am
Doesn't work. Text still remains colored. The only possible way I found so far is take only actual word where the cursor stands a make Syntax highlighting on it. The rest remains as it is.
It's good for one phrase keywords like public static etc. but if you want colorize string: "this is colorized string"
or comments: // this is definitely green text
then I'm lost.
I have a syntax coloring in old .NET RichTextBox, where you can search your words three times there and back again, colorize it and it's fast enough, here if you color more than 3 words it's totally unusable…
If anyone found a better solution, please share with us
January 1st, 2009 at 12:03 am
Try instead of documentRange.ClearAllProperties();
just set the background and foreground colors of the whole FlowDocument to white and black
January 1st, 2009 at 12:03 am
this is the row: that slow things down:
documentRange.ClearAllProperties();
January 1st, 2009 at 12:03 am
Good work, but it's so horribly slow…
January 1st, 2009 at 12:03 am
You don't need to define something called "Tag". You can use TextRange instead, which serves for the same.
See this sample: blogs.msdn.com/…/navigate-words-in-richtextbox.aspx
January 1st, 2009 at 12:03 am
RichTextBox 中文字背景高亮方法的一些link
January 1st, 2009 at 12:03 am
great sample–just what i was looking for, thanks
January 1st, 2009 at 12:03 am
it’s low what?
January 12th, 2009 at 8:26 am
For anyone landing here looking for WPF Richtextbox syntax highlighting, I can confirm that the above is not a solution for any meaningful size of file. I previously used ScintillaNet for this purpose but cannot get it working under 64bit Vista. Having decided I only need some basic highlighting and not folding, block comment colouring etc I am experimenting (with some success) a more efficient version of the above concept.
Perhaps more determined and capable programmers than me might find this concept worth exploring….
1. Concept …. Use the Paragraph.Tag flags in the document to indicate that a paragraph needs highlighting or re-highlighting… use NULL (the default) to indicate it does need highlighting that way newly pasted text is handled.
2. In the TextChangedEventHandler use the change list (with the Event Arg e) to mark each unique paragraph mentioned as needing re-highlighting follows…
foreach (TextChange C in e.Changes)
{
TextPointer P = Document.ContentStart.GetPositionAtOffset(C.Offset);
if (P.Paragraph != null) P.Paragraph.Tag = null; //It needs reformatting
}
ReformatAsNeeded()
3. Call reformat as needed at the end of the Text change event and when the file is first loaded (this bit is slow)
void ReformatAsNeeded()
{
if (Document.Blocks.Count > 0)
{
Block B = Document.Blocks.FirstBlock;
while (B != null)
{
if (B is Paragraph)
{
Paragraph P = B as Paragraph;
if (P.Tag == null)
{
ReFormat(P);
P.Tag = true; //It has been highlighted
}
}
else throw new Exception(“Unknown block type ” + B.ToString());
B = B.NextBlock;
}
}
4. Modify the “navigator” code in the article to only walk the Paragraph supplied via the Reformat call shown above.
Initial highlighting a newly loaded file is still slow (it probably always will be using this technology!) but I can live with that for my files. As said this is experimental work in progress, it may not work… is probably too early to mention, but posting now as it may trigger ideas. Yes I know there are still bugs in it (from the original) like does not colour a word on the end of line.
PS – Please excuse the possible poor formatting.
October 9th, 2009 at 4:52 pm
By the way why are you using bitwise or | instead of conditional or ||? For example here
Char.IsWhiteSpace(text[i]) | JSSyntaxProvider.GetSpecials.Contains(text[i])
This will evaluate the second expression even if the first is true. If you use || and the first is true (character is white space) it will not waste time to look at the second. Obviously character cannot be white space and special at the same time
October 12th, 2009 at 10:29 pm
Here is one alternative solution: http://blog.bodurov.com/Wpf-Source-Code-Editor