There are hundreds of editors to choose from out on the market(SlickEdit, CodeWright, Visual Studio, SharpDevelop), so the goal of this article is not to replace them, but to show you how to manipulate the RichEditBox to create color syntax as well as show you one way to print the RichEditBox to the printer. For some reason, I got caught up doing some PHP coding, so I decided to focus coloring this editor according to PHP, a popular and efficient language for scripting inside of HTML.
Figure 1 Print Preview of RichTextBox ColorSyntax Editor
Figure 2 The Color Syntax Editor Application
The Application is a simple Windows Form program that allows you to open and save the text files edited in the rich edit control, as well as print them out. The program takes advantage of a syntax text file for PHP which lists the functions and keywords contained in the PHP language. Below is a sample of the syntax file. The file consists of 3 headers: KEYWORDS, FUNCTIONS, and COMMENTS. The Application assigns colors to the keywords based on their specified category:
The file is read in using the ColorSyntaxEditor class shown as part of the design below.As seen in figure 3, the design consists of a Form class containing the GUI and FlickerFreeRichEditTextBox, a SyntaxReader class for reading language syntax from a file, and a WordAndPosition class used to hold information about words and positions of those words in the RichTextBox:
Figure 3 UML Design of Color Syntax Editor reverse engineered using WithClass UML Tool for C#
One thing you may be curious about is how do we assign different colors to individual words inside the RichTextBox?.The way this is done is through a trick that selects the word we are interested in coloring and then assigns a color to the selection (you can also assign a font to the selection if you wish):
Listing 1 Coloring in a Selection in the RichTextBox
- Color c = Lookup(wp.Word);
- richTextBox1.Select(wp.Position, wp.Length);
- richTextBox1.SelectionColor = c;
Editing the text has the following use case it uses to change the color:
- Event Handler traps that the RichTextBox text has changed
- Parse the current line of text that the cursor is sitting on
- Parse each word in the line using a Regular Expression object and save words in an array.
- Select each parsed word and color according to the ColorSyntaxEditor.
Below is the method that implements this use case:
Listing 2 Coloring in words and symbols on the current line
- private void MakeColorSyntaxForCurrentLine()
- {
-
- int CurrentSelectionStart = richTextBox1.SelectionStart;
- int CurrentSelectionLength = richTextBox1.SelectionLength;
-
- int pos = CurrentSelectionStart;
- while ((pos > 0) && (richTextBox1.Text[pos - 1] != '\n'))
- pos--;
-
- int pos2 = CurrentSelectionStart;
- while ((pos2 < richTextBox1.Text.Length) && (richTextBox1.Text[pos2] != '\n')) pos2++;
- string s = richTextBox1.Text.Substring(pos, pos2 - pos);
-
- int count = ParseLine(s);
-
-
- for (int i = 0; i < count; i++)
- {
- WordAndPosition wp = TheBuffer[i];
- Color c = Lookup(wp.Word);
- richTextBox1.Select(wp.Position + pos, wp.Length);
- richTextBox1.SelectionColor = c;
- }
-
- if (CurrentSelectionStart >= 0)
- richTextBox1.Select(CurrentSelectionStart, CurrentSelectionLength);
- }
Comments of course need to be handled a bit differently so a comment case looks like the following:
- TextChanged Event Handler traps RichTextBox event
- Extract the current line that the cursor sits on
- if next symbol is a comment, color the entire line to the comment color
The code for coloring the comment is shown below:
Listing 3 Coloring in comments in the RichTextBox
-
- if (wp.Word == "/" && previousWord == "/")
- {
-
- int posCommentStart = wp.Position - 1;
- posCommentEnd = pos2;
-
-
- richTextBox1.Select(posCommentStart + pos, posCommentEnd -
- (posCommentStart + pos));
- richTextBox1.SelectionColor = this.kCommentColor;
- }
How do we parse the words in a line? The words of code need to be parsed using a regular expression that pulls out entire words such as(php, echo, var) as an element to color and symbols (such as .;?+) as an element to color. The regular expression that does this is shown in the code below:
Listing 4 A regular expression for parsing words and symbols
- Regex r = new Regex(@"\w+|[^A-Za-z0-9_ \f\t\v]",
- RegexOptions.IgnoreCase|RegexOptions.Compiled);
This line of code creates a regular expression object that accepts either words or symbols.The alphanumeric words are filtered in using the \w expression. The symbols are filtered in using the
[^A-Za-z0-9 \f\t\v] expression which basically says only accept a single character that is not an alphanumeric or whitespace characters.
The code for parsing a line into words and symbols and their corresponding positions is shown below:
Listing 5 Matching the regular expression against the words in the line of code
- private int ParseLine(string s)
- {
-
- TheBuffer.Initialize();
- int count = 0;
-
- Regex r = new Regex(@"\w+|[^A-Za-z0-9_ \f\t\v]", RegexOptions.IgnoreCase | RegexOptions.Compiled);
- Match m;
-
-
- for (m = r.Match(s); m.Success; m = m.NextMatch())
- {
- TheBuffer[count].Word = m.Value;
- TheBuffer[count].Position = m.Index;
- TheBuffer[count].Length = m.Length;
-
- count++;
- }
-
- return count;
- }
Reducing the Flicker
A few individuals have been kind enough to e-mail me a solution on how to reduce the flicker in this application (Mark Mihevc & JACK DUNN) and I just wanted to take the opportunity in this article to thank them. In order to reduce the flicker of the RichTextBox, we simply subclass the RichTextBox class with our own called FlickerFreeRichEditTextBox. Then we override the WndProc method in this class to give us control over when the WM_PAINT message is passed into the class. The entire subclass of the rich text box control is shown below:
Listing 6 Subclassing the RichTextBox to reduce flicker in the control
- using System;
- using System.Windows.Forms;
- namespace ColorSyntaxEditor
- {
-
-
-
- public class FlickerFreeRichEditTextBox : RichTextBox
- {
- const short WM_PAINT = 0x00f;
- public FlickerFreeRichEditTextBox()
- {
- }
- public static bool _Paint = true;
- protected override void WndProc(ref System.Windows.Forms.Message m)
- {
-
-
-
- if (m.Msg == WM_PAINT)
- {
- if (_Paint)
- base.WndProc(ref m);
- else
- m.Result = IntPtr.Zero;
- }
- else
- base.WndProc(ref m);
- }
- }
- }
To use the control above, simply change the static _Paint variable to false when we select and color the text and turn it back to true when we are done. Walla! The flicker disappears. Below are the calls in our code that toggles the _Paint flag in the RichTextBox's TextChanged Event Handler inside our Form.
Listing 7 Toggling the _Paint flag of the subclassed RichTextBox to reduce flicker
- private void richTextBox1_TextChanged(object sender, System.EventArgs e)
- {
- if (populating)
- return;
- ColorSyntaxEditor.FlickerFreeRichEditTextBox._Paint = false;
- MakeColorSyntaxForCurrentLine();
- ColorSyntaxEditor.FlickerFreeRichEditTextBox._Paint = true;
- }
Part II Printing the RichTextBox
We can some of the same techniques previously mentioned in this article to help us in printing out the rich text box. Remember from previous articles in C# Corner on printing that all printing is performed from the PrintPage EventHandler produced by double clicking on the printDocument1 component. (See
Printing in C#). From the PrintPage event handler, we can get a handle to the device context of the printer. We then pass this Graphics object to our GDI+ method that prints the RichEditControl.
- private void printDocument1_PrintPage(object sender, System.Drawing.Printing.PrintPageEventArgs e)
- {
-
- Graphics g = e.Graphics;
-
-
- lastChar = DrawEditControl(g, lastChar);
- if (lastChar != -1)
- e.HasMorePages = true;
- else
- {
- e.HasMorePages = false;
- lastChar = 0;
- }
- }
Note also that this method checks to see if we have multiple pages to print. If we do, the
HasMorePages is set to true and the PrintPage event will be triggered again. Now lets take a look at the
DrawEditControl method which renders the contents of the RichTextBox control:
- private int lastChar = 0;
- private int DrawEditControl(Graphics g, int lastChar)
-
-
- int xPos = 10;
- int yPos = 40;
- int kMargin = 50;
-
- for (int c = lastChar; c<richTextBox1.Text.Length; c++)
- {
-
- richTextBox1.Select(c,1);
- char nextChar = richTextBox1.Text[c];
- Color theColor = richTextBox1.SelectionColor;
- Font theFont = richTextBox1.SelectionFont;
-
- int height = theFont.Height;
-
-
- if (nextChar == '\n')
- {
-
- yPos += (height + 3);
- xPos = 10;
-
- int paperHeight = printDocument1.PrinterSettings. DefaultPageSettings.PaperSize.Height;
-
- if (yPos > paperHeight - kMargin)
- return c;
- }
-
-
- else if ((nextChar == ' ') || (nextChar == '\t'))
- {
- xPos += theFont.Height/2;
- }
- else
- {
- Regex r = new Regex(@"\w", RegexOptions.IgnoreCase | RegexOptions.Compiled);
- Match m;
- string nextWord = "";
- bool reduceAtEnd = false;
- m = r.Match(nextChar.ToString());
-
- if (m.Success)
- reduceAtEnd = true;
- else
- nextWord = nextChar.ToString();
-
-
-
-
-
- while (m.Success)
- {
- nextWord += nextChar;
- c++;
- nextChar = richTextBox1.Text[c];
- m = r.Match(nextChar.ToString());
- }
- if (reduceAtEnd)
- {
- c--;
- }
-
-
- g.DrawString(nextWord, theFont, new SolidBrush(theColor),xPos, yPos);
-
-
- SizeF thesize = g.MeasureString(nextWord, theFont);
-
- xPos += (int) thesize.Width - 4;
- }
- }
-
- return -1;
The code above goes character by character in the RichEditBox, selects the character, and uses the selection to determine the color and font. The characters are rendered to the printer using the Graphics method DrawString.Whole words are grouped using a regular expression before being sent to draw string to make a more aesthetic and accurately portrayed print representation. The cursor is advanced either horizontally or vertically according to the current character. Return characters will advance the current drawing position vertically to the beginning of the next line. All other characters advance the current drawing position horizontally.
Conclusions and Observations
The color syntax editor for PHP is a simple tool to help you understand how to utilize the RichTextBox control. You can edit the syntax file read by the SyntaxReader class to support C#, C++, Java, VB or any language you want. It would be nice to have a way of setting a characters color in the rich edit control according to position rather than selection. I noticed also that the rich edit control can be streamed out into a file that maintains its color and font information. This probably would be a nice future feature for this app.