Click here to Skip to main content
Click here to Skip to main content

(Improved) Fast syntax highlighting control!

By , 1 Feb 2013
Rate this:
Please Sign up or sign in to vote.

Testing the syntax highlighting!

Introduction 

Hello CodeProject! I am back once again. This time I have removed my other article, it was my first article so besides it being a really complicated and long one, I now have finally improved the code. No more bugs as far as I can see, the speed of the control has now increased roughly by 35-45%. If you have never seen my previous article, well, basically this is my very own Syntax Highlighting control. It's derived from RichTextBox, and I suppose you all have seen the results of many people who made these ones. Well, no worries, I have made sure this one actually fulfills any needs or desires (isn't it the same? :P).

The code

This code that I will give you has been completely made by me. The idea's however have been inspired by many Notepad's (Notepad++, Visual Studio's IDE). This is a basic piece of code, and anyone who has the ability to understand this simple code should be able to do as he/she wants easily. If you use this control, make sure to make something good out off it, I don't need any (Credits?), as this has purely been made for learning purposes by me.

How do you use the code?

  1. Create a new Project or use an existing one.
  2. Add a new class by going to your Solution Explorer, Add, New Item, Class.
  3. Replace the class code with the following.
  4. Build or debug the application. 
  5. (Drag)(Create) (in) the TextBox (or make one) (in) via (code) (the ToolBox).
  6. Profit!

The code

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;
using System.Drawing;
using System.Threading;

namespace Classik
{
    public class AdvancedRichTextBox : RichTextBox
    {

        #region API Stuff
        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        public static extern int GetScrollPos(IntPtr hWnd, int nBar);

        [DllImport("user32.dll")]
        public static extern int SetScrollPos(IntPtr hWnd, int nBar, int nPos, bool bRedraw);

        [DllImport("user32.dll")]
        public static extern bool LockWindowUpdate(IntPtr hWnd);

        private const int SB_HORZ = 0x0;
        private const int SB_VERT = 0x1;
        #endregion
        public int HorizontalPosition
        {
            get { return GetScrollPos((IntPtr)this.Handle, SB_HORZ); }
            set { SetScrollPos((IntPtr)this.Handle, SB_HORZ, value, true); }
        }
        public int VerticalPosition
        {
            get { return GetScrollPos((IntPtr)this.Handle, SB_VERT); }
            set { SetScrollPos((IntPtr)this.Handle, SB_VERT, value, true); }
        }

        public AdvancedRichTextBox()
        {
            this.ForeColor = Color.Black;
        }

        int PreviousLength = 0;
        protected override void OnTextChanged(EventArgs e)
        {
            if (PreviousLength == this.Text.Length - 1 || PreviousLength == this.Text.Length + 1)
                SyntaxView(new Regex("( for | while )"));
            else
                SyntaxAll(new Regex("( for | while )"));

            PreviousLength = this.Text.Length;

            base.OnTextChanged(e);
        }

        public void SyntaxView(Regex Syntax)
        {
            int SelectionStart = this.SelectionStart;
            int SelectionLength = this.SelectionLength;

            int FirstCharacter = this.GetCharIndexFromPosition(new Point(0, 0));
            int FirstLine = this.GetLineFromCharIndex(FirstCharacter);

             int LastLine = this.GetLineFromCharIndex(this.GetCharIndexFromPosition(
                 new Point(this.ClientSize.Width, this.ClientSize.Height))) ;

            Color SelectionColor = this.ForeColor;
            Font SelectionFont = this.Font;

            for (int i = FirstLine; i <= LastLine; i++)
            {
                MatchCollection Matches = Syntax.Matches(this.Lines[i]);

                if (Matches.Count > 0)
                {
                    LockWindowUpdate(this.Handle);

                    foreach (Match m in Matches)
                    {
                        int Start = this.GetFirstCharIndexFromLine(i);
                        this.Select(Start + m.Index, m.Length);
                        this.SelectionColor = Color.Red;
                    }
                }
            }

            this.SelectionStart = SelectionStart;
            this.SelectionLength = SelectionLength;
            this.SelectionColor = SelectionColor;
            this.SelectionFont = SelectionFont;

            LockWindowUpdate(IntPtr.Zero);

            this.Invalidate();

        }
        public void SyntaxAll(Regex Syntax)
        {
            int SelectionStart = this.SelectionStart;
            int SelectionLength = this.SelectionLength;

            Color SelectionColor = this.ForeColor;
            Font SelectionFont = this.Font;

            LockWindowUpdate(this.Handle);

            for (int i = 0; i < this.Lines.Length; i++)
            {
                MatchCollection Matches = Syntax.Matches(this.Lines[i]);

                if (Matches.Count > 0)
                {
                    LockWindowUpdate(this.Handle);

                    foreach (Match m in Matches)
                    {
                        int Start = this.GetFirstCharIndexFromLine(i);
                        this.Select(Start + m.Index, m.Length);
                        this.SelectionColor = Color.Red;
                    }
                }
            }

            this.SelectionFont = SelectionFont;
            this.SelectionColor = SelectionColor;
            this.SelectionStart = SelectionStart;
            this.SelectionLength = SelectionLength;

            LockWindowUpdate(IntPtr.Zero);

            this.Invalidate();
        }
    }
}

Using the code

Now, how would we go about using this highlighting tool. Well, I have made some simple steps "The Code" box,  it will explain how to add the code and use it. But! There are some other things mainly, how would you go about adding more highlights! Well, it's easy. We have two voids, SyntaxView and SyntaxAll. They both need to be changed in order to make it work. The only bits you have to concentrate on is:

LockWindowUpdate(this.Handle);

foreach (Match m in Matches)
{
    int Start = this.GetFirstCharIndexFromLine(i);
    this.Select(Start + m.Index, m.Length);
    this.SelectionColor = Color.Red;
}

and:

SyntaxView(new Regex("( for | while )"));
else
    SyntaxAll(new Regex("( for | while )"));

The smallest one will hopefully explain itself. If it does not, basically, every single word you would like to be highlighted needs to be in the new Regex, split by '|'. The above code is in both of the voids. And it can be used to change the color of highlighting. You can also change the font using 'this.SelectionFont'. There is another things that you might wonder: "How would I add more Colors instead of only one", well this is easy. All you have to do is add a little Switch or if statement to both of the foreach loops like so;

Color HighlightColor = Color.Blue;
switch (m.ToString())
{
    case "one":
       HighlightColor = Color.Blue;
       break;

    case "two":
       HighlightColor = Color.Orange;
       break;

    case "three":
       HighlightColor = Color.Purple;
       break;
}
this.SelectionColor = HighlightColor ;

There are of course a lot of other ways of doing it. This is just a simple example. But make sure to change both the view and all.

Points of Interest

I think the most interesting part of this article is that it's simple to use, and easy for everyone to understand.

Additional Notes

There are a few external API voids, some of them should explain themselves, one does not:

  • LockWindowUpdate: This makes the window frozen until unfrozen with 'IntPtr.Zero'. You use handles for that one.

I am sorry if anyone had troubles reading this, English is not my first language so I hope the article has been interesting and especially understandable.

History 

  • Fixed every bug known in older versions. 
  • 1/2/2013: Fixed 'Highlighting for in foreach' bug. Cheers wvd_vegt.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Yvar Birx

Netherlands Netherlands
Yvar Birx;
 
I am fifteen years old and studied coding for about three years. I mostly code in C#, but I also have coded in Java and Visual Basic.

Comments and Discussions

 
QuestionLooks Good & Next Steps PinmvpDave Kerr28-Nov-13 21:10 
QuestionWhere is the download For project? PinmemberElegiac20-May-13 18:20 
GeneralMy vote of 4 Pinmemberwvd_vegt1-Feb-13 4:47 
GeneralRe: My vote of 4 [modified] Pinmemberwvd_vegt1-Feb-13 4:53 
GeneralRe: My vote of 4 PinmemberYvar Birx1-Feb-13 5:37 
GeneralRe: My vote of 4 [modified] Pinmemberwvd_vegt1-Feb-13 8:05 
Hi
 
Spaces around the regex are mist of the time not a good idea. Threy do not match tabs or other charactes like a { directly after a keyword. Thats why the special regex markers kike \b are added to regex,
 
As for lockwindowupdate, in the second routine you call it once before the for loop and inside it,both with a handle as parameter. This is not supported and incorrect. It seems to work but still is incorrect, read msdn,
 
Lockwindowupdate calls should be called with a zero parameter before passing a handle again. The first call saves the windowhandle internally and the call witn 0 restores it. In your case it seems to work because the handle is the same but its bad as an example.
 
Whe you type text in the control containng keywords it still highlights incorrect. And as formatting isn't removed before applyimg new that also gives problems with typing. It however works fine for already present text.
 
[edit] if you want to use spaces it's much more efficient to use
 
new Regex(@" (for|static|private|public|int|string|while|void) ");
 
This regex however matches even 'for{', 'for' at the end of a line but not 'foreach':
 
new Regex(@"\b(for|static|private|public|int|string|while|void)\b");
 
Also remember to put a @ in front of the string or you have to escape some characters in the string if regexes are becoming more complex. You should read some more on regex, they are not as simple as you think.
 
I also rewrote the code so both Syntax methods call a single method do do the coloring.
And added
 
Select(StartIndex, Length);
this.SelectionColor = this.ForeColor;
 
just before the for loop doing the coloring. The result is that old coloring is removed. It needs an additional line just before restoring the SelectionFont/Color to prevent the last colored word from becoming black again.
 
Select(this.SelectionStart, 0);
 
Regards
wvd_vegt


modified 1-Feb-13 14:40pm.

GeneralRe: My vote of 4 PinmemberYvar Birx2-Feb-13 4:02 
GeneralBut where's the code? PinmemberJohn Brett31-Jan-13 1:02 
GeneralRe: But where's the code? PinmemberYvar Birx31-Jan-13 4:41 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web01 | 2.8.140415.2 | Last Updated 1 Feb 2013
Article Copyright 2013 by Yvar Birx
Everything else Copyright © CodeProject, 1999-2014
Terms of Use
Layout: fixed | fluid