Monday, July 27, 2009

WPF Text Highlighting

WPF Text Highlighting:

Yesterday i tried to do some basic text Highlighting (Syntax) for my project, I wanted to use WPF but i didn't manage to find anything useful on the Internet.

I did found an start point though in this article, but the solution presented there was very very slow after 2 key words the WPF text box did choke, and die.

It turns out that the WPF Syntax Highlighting by using the RichTextBox is quite of a problem as oposed to the WinForms version.

So I made a decision that it's time to make my own implementation that would really work :-)

So Boys and Girls Here We GO:

// I have found this control and this even handler,
// a total mess, nothing works as it should and weird
// things can happen so all of those checks in the method
// are to ensure the correct event data.

//global value that represents our word.
private string value = string.Empty;

/// <summary>
/// Raises an event when the text is changed,
/// this even is used to handle syntax colorization.
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="e">The <see cref="System.Windows.Controls
///.TextChangedEventArgs"/> instance containing the event data.</param>
private void TextChangedEventHandler(object sender,
TextChangedEventArgs e)
//We shoul only take the last change 
//as it can happen that we will have severall,
//and only one will be the proper one
//the rest will point to offset 1,
//so the latest offset will be the highest one.
TextChange change = (from x in e.Changes where
x.Offset == e.Changes.Max(p => p.Offset)
select x).FirstOrDefault();

//Text change can also be null sometimes
//so take only the proper one.
if (change != null && change.Offset != 0)
//Get the range of the key that was pressed
//TO CONSIDER: probably this could be done better by
//not adding the text to the value but increase the
//end pointer
//and produce words.
TextRange range = new TextRange(this.TextInput.Document
.ContentStart.GetPositionAtOffset(change.Offset - 1)
, this.TextInput.Document.ContentStart.

//We need to manage the text adn the value
//to produce the correct words.
if (range.Text != " ")
if (change.AddedLength > 0)
value += range.Text;
else if (change.RemovedLength > 0)
if (value.Length != 0)
value = value.Remove(value.Length - 1);
value = string.Empty;

//Our main method for syntax colloring
//basicly all of the coloring will be done here.
TryColorSyntax(value, change);
value = string.Empty;

/// <summary>
/// Tries the color syntax.
/// </summary>
/// <param name="value">The value.</param>
/// <param name="current">The current.</param>
private void TryColorSyntax(string value, TextChange current)
//THIS IS IMPORTANT, as text range applypropertyvalue will
//sometimes (not always!) trigger
//the text change event handler and that would break our
//value as it would give us the same letter
//as we had a few times so the lenght would be broken
//thus resulting in an exception.

//So what we do is, we unregister the event and then proceed.
TextInput.TextChanged -= this.TextChangedEventHandler;

TextRange r = new TextRange(this.TextInput.Document.ContentStart
.GetPositionAtOffset(current.Offset - 1 - value.Length)
, this.TextInput.Document.ContentStart

//Put your detection code here:
//below is an simple example for my syntax

if (value.Contains("@") && value.Length > 1)
new SolidColorBrush(Colors.GreenYellow));
else if (value.Contains("{") && value.Contains("}"))
new SolidColorBrush(Colors.Purple));
{   //default value is black
new SolidColorBrush(Colors.Black));

//Then we register the event again.
TextInput.TextChanged += this.TextChangedEventHandler;

This concept could be expanded more so you can do pretty robust and Fast syntax Highlighting solution with WPF rich text box without the need of creating your own controls and text renders.

Hope you like the code :-)

if you have any questions post a comment.

Thank you