Javaв„ў EE 5 Tutorial, The (3rd Edition)

Our XMLNotepad spike worked well enough to make us think we should start putting it in the right shape. That didn t turn out quite like we expected...

Where We Were

We had a little XMLNotepad prototype working: we could edit in the window, and when we hit Enter, we got a new section with paired P tags. We hadn t yet put the cursor where we wanted it, between the tags, but we knew how to do that. Now we thought we should put the code in better order. Here s what it looked like when we started, with a few debugging additions compared to the last time you saw the code:

using System; using System.Drawing; using System.Windows.Forms; using Nunit.Framework; using System.Collections; using System.Text.RegularExpressions; namespace Notepad { class XMLNotepad : NotepadCloneNoMenu { static void Main(string[] args) { Application.Run(new XMLNotepad()); } public XMLNotepad() { Text = "XML Notepad"; txtbox.KeyDown += new KeyEventHandler(XMLKeyDownHandler); txtbox.KeyPress += new KeyPressEventHandler(XMLKeyPressHandler); } private int CursorLine() { String[] lines = txtbox.Lines; txtbox.SelectionLength = 0; int start = txtbox.SelectionStart; int length = 0; int lineFound = 0; int lineNr = 0; foreach ( String s in lines) { if (length <= start && start <= length+s.Length + 2 ) { lineFound = lineNr; } length += s.Length + 2; lineNr++; } return lineFound; } void XMLKeyPressHandler(object objSender, KeyPressEventArgs kea) { int key = (int) kea.KeyChar; if (key == (int) Keys.Enter) { kea.Handled = true; } } void XMLKeyDownHandler(object objSender, KeyEventArgs kea) { if (kea.KeyCode == Keys.P && kea.Modifiers == Keys.Control) { txtbox.Text += "controlP"; kea.Handled = true; } if (kea.KeyCode == Keys.Enter) { String[] lines = txtbox.Lines; Console.WriteLine("LineCount {0}", txtbox.Lines.Length); int cursorLine = CursorLine(); String[] newlines = new String[lines.Length+2]; for(int i = 0; i < = cursorLine; i++) { newlines[i] = lines[i]; } newlines[cursorLine+1] = ""; newlines[cursorLine+2] = " < P >< /P > "; for (int i = cursorLine+1; i < lines.Length; i++) { newlines[i+2] = lines[i]; } txtbox.Lines = newlines; kea.Handled = true; Console.WriteLine("LineCount {0}", txtbox.Lines.Length); } // debugging keys if (kea.KeyCode == Keys.L && kea.Modifiers == Keys.Control) { String[] lines = txtbox.Lines; foreach ( String s in lines) { Console.WriteLine(s); } kea.Handled = true; } if (kea.KeyCode == Keys.S && kea.Modifiers == Keys.Control) { txtbox.SelectionLength = 0; kea.Handled = true; } if (kea.KeyCode == Keys.Q && kea.Modifiers == Keys.Control) { Console.WriteLine("cursor: {0}", CursorLine()); kea.Handled = true; } } } }

You ll note a few debug lines just above. Ctrl+S sets the selection length to zero, and Ctrl+Q displays the current cursor position. I used these to get an understanding of how the TextBox handles the cursor position and the selection. Nothing to see here, move on.

This isn t a very good design. All our operational code is in the Form object, in the KeyDownHandler, and we re working directly with the TextBox. The single Form object is handling the display of the data, the keyboard processing, and the actual text processing. Objects should have one and only one purpose, so this is two too many. Chet and I set out to create a new object to serve as the model in a Model View Controller (MVC) triad . [1] Our problems turned out to be many. First, we are too new with C# to be sure how (or even whether) to set up MVC in .NET. Second, we aren t facile enough yet with events and delegation to be sure how to proceed. Still, we need to clean up this code, and this will be a chance to show you how we clean up a design without having a grand design objective in mind.

[1] MVC is one of a handful of well-known patterns for separating GUI functionality from system functionality. Its details are out of scope for us here, especially since we didn t use the pattern. As you ll see, we do wind up, over time, with a fairly good separation of functionality.

Категории