Login | Register   
RSS Feed
Download our iPhone app
Browse DevX
Sign up for e-mail newsletters from DevX


.NET Building Blocks: Build a RichTextBox-Based Syntax-Highlighting Editor and IDE  : Page 6

By using the RichTextbox as a base control, you can build an editor that highlights keywords and quoted strings—or even an editor that employs IDE-like smart indentation and runs script code interactively.

Creating Your Own Language-Aware IDE
Now that you have a language-aware editor, the next step is to use that as a building block for your IDE. This part of the walk-through takes the editor control you have just created and allows you to execute some or all of its contents within the program.

Step 1: Create a Project
Figure 22. Adding Same-Project References: Unlike earlier passes at adding references, here you are adding one from within the same solution. Visual Studio conveniently lists those on the Projects tab so you don't have to browse.
Add a new project to your solution (or just start a new solution) that uses the Windows Application template. You'll find the finished application as the VBScriptTestPlatform project in the downloadable code that accompanies this article.

Step 2: Add a VBScriptSyntaxHilightTextBox for Input
Add a reference to your new control (right-click on References in the Solution Explorer) to make it available (see Figure 22).

The VBScriptSyntaxHilightTextBox control will show up in your toolbox if you added the new project within the same solution. If not, add it by right-clicking in the toolbox and select Choose Items…, and then drag a VBScriptSyntaxHilightTextBox onto your form. The control will act as the input control for you VBScript language editor.

Step 3: Add a StyleAwareRichTextBox for Output
When you added controls to your toolbox at the beginning of the article (from the CleanCodeControls library), one of those added was a StyleAwareRichTextBox. If it is not in your toolbox, add it now. Drag a StyleAwareRichTextBox onto your form. (See the StyleAwareRichTextBox API for more details.) This control acts as a console for messages and execution output. The earlier demo used a DataGridView, which was appropriate for SQL output, but here you need a scrolling text window.

Figure 23. VBScript Editor: A trial run of the VBScriptIdeDemo showing the automatic highlighting that occurs after typing in some code.
Step 4: Test the Program
At this point you could run the program just to see how the VBScript editor works. Type in some code and you will see it highlight comments, keywords, and strings, as shown in Figure 23. (If you have the AutoHilight property on, this will happen as you type; if not press Control-Shift-H to highlight on demand.)

Step 5: Add Other Bells and Whistles
The sample application includes a Button and a SplitContainer just as in the earlier demo. Finally, I have added a CheckBox labeled "Clear on Execute" whose purpose is to empty the output window each time your press the Execute button. That makes the output less cluttered, but making it user-selectable gives the user a choice of whether to start each program run with a cleared output pane.

Step 6: Write the Execution Code
The top pane on the form provides your VBScript editor. The bottom pane provides your output window. So all that you need to do is implement the code behind the Execute button. Note that you actually needed no (user-written) code at all for just the editor. All the code in this demo project is for executing the Visual Basic code and displaying the results in the output window. It is quite brief; here's the entire program:

public partial class Form1 : Form { private const string SCRIPT_ENGINE = "cscript.exe"; private string tempfile; private CleanCode.IO.ExecProcess runner; public Form1() { InitializeComponent(); tempfile = Path.Combine(Path.GetTempPath(), "temp.vbs"); runner = new CleanCode.IO.ExecProcess( SCRIPT_ENGINE, tempfile); } private void executeButton_Click(object sender, EventArgs e) { if (clearOnExecuteCheckBox.Checked) { outputTextBox.Clear(); Refresh(); } List<string> lines = RunCode(); // Post-process output to highlight errors. string prefix = runner.ErrPrefix; foreach (string s in lines) { if (s.StartsWith(prefix)) { string errMsg = s.Substring(prefix.Length); if (errMsg.StartsWith(tempfile)) { errMsg = "Line " + errMsg.Substring(tempfile.Length); } outputTextBox.AppendStylizedText( errMsg + Environment.NewLine, Color.Red); } else { outputTextBox.AppendText(s + Environment.NewLine); } } } private List<string> RunCode() { // Run process and collect output. File.WriteAllText(tempfile, vbScriptTextBox.SelectedOrAllText); runner.CollectJointOutput = true; runner.RunProcess(); File.Delete(tempfile); return runner.JointOutput; } }

This code consists of just a constructor, an event handler for the button, and one support method. The RunCode method is a small but powerful routine that takes all or part of your input VBScript text, executes it, and returns both the stdout and stderr output streams. The executeButton_Click event handler takes that output and fills the output window in the following fashion: regular output (i.e. from the stdout stream) appears in the default font color (in my demo, this is white on a black background), while any unusual output (from the stderr stream) appears in red. Figure 24 shows how the output pane highlights a compilation error, while Figure 25 shows a run-time error. Note that Figure 25 shows partial output from the program before it encountered the run-time error. The StyleAwareRichTextBox used for the output pane supports the color-coding, which is why I used that control rather than a standard RichTextBox or TextBox.

Figure 24. Compilation Error Output: Pressing Execute with the code shown yields a compilation error because of the extraneous "x" on the final line.
Figure 25. Runtime Error Output: Pressing Execute with this code, on the other hand, yields a runtime error because of the misspelled "echoo" method.
You'll find the ExecProcess class used to execute VBScript code in the CleanCode library download. The ExecProcess class runs any external program and returns its output streams, either as a single, combined output stream as shown here, or as individual stdout and stderr output streams. If instead of a single output pane, for example, you wanted to have one output pane for regular output and a separate window, tab, pop-up, etc. for error output, you could instead instruct the class to collect individual streams and return them separately. See the ExecProcess API for more details.

To execute a VBScript program, you use the Windows Script Host engine (cscript.exe) by passing it a file name on the command line. So the RunCode method creates a temporary file with the user's selected text. After it runs the code it deletes the temporary file. Note that the constructor includes a line of code that initializes the ExecProcess instance, telling it what program and what file to run.

The Execute button event handler performs these steps:

Figure 26. Successive Runs: In this example, the Clear on Execute checkbox is not checked, so the output from successive executions accumulates in the output pane.
  • It first checks whether the user checked "Clear on Execute." If so, it erases the output window before refilling it. Figures 24 and 25, you will note, show output with the box checked; that way the output is limited to just the current run. Figure 26, on the other hand, has the box unchecked, so you can retain past runs in the output window.
  • Next, it runs the VBScript code entered by the user by invoking RunCode.
  • After it has the output in hand, it goes through it line by line, deciding whether it is tagged as a line from stderr or not. (When using a combined stream, the ExecProcess class tags lines from stderr with the ErrPrefix property string so you can distinguish them from stdout lines.)
  • If the line is tagged (indicating a line from stderr), the tag is removed. Next, the line is further pruned to remove the full path and name of the temporary file that the program used—no need to bother users with that irrelevant information. Finally, it uses a feature of the StyleAwareRichTextBox to color the line to differentiate it from standard output.
  • If the line is not tagged, the method simply outputs it directly to the window.
Possible Uses
I hope you can see how the controls and Visual Studio create the potential for simple yet elegant designs. You could, for example, create an IDE for the dual code-and-WYSIWYG HTML editor I mentioned at the beginning of the article. With key building blocks such as the VBScriptSyntaxHilightTextBox, StyleAwareRichTextBox, and ExecProcess classes, you can craft an editor or a basic IDE quickly. I think you'll find these to be powerful user interface components for many types of programs.

Michael Sorens is a freelance software engineer, spreading the seeds of good design wherever possible, including through his open-source web site, teaching (University of Phoenix plus community colleges), and writing (contributed to two books plus various articles). With BS and MS degrees in computer science and engineering from Case Western Reserve University, he has worked at Fortune 500 firms and at startups, using C#, SQL, XML, XSL, Java, Perl, C, Lisp, PostScript, and others. His favorite project: designing and implementing the world's smallest word processor, where the medium was silicon, the printer "head" was a laser, and the Declaration of Independence could literally fit on the head of a pin. You can discuss this or any other article by Michael Sorens here.
Comment and Contribute






(Maximum characters: 1200). You have 1200 characters left.