A Minor Setback on the Way to Success


However, I still don t trust the tests completely. This is not a good thing, but it s a true thing. And rightly so: running the notepad manually, I notice some odd behavior when using the arrow keys to cursor around. That complex code to classify whether to do a single-character snapshot is snapping on an arrow key. It turns out that the arrow keys come in looking like shifted numeric characters , % and such. This called my attention to the fact that we have both KeyPress and KeyDown handlers. I like the KeyDown, because it allows us to use the Keys enumeration. On the other hand, we were left with that one check for Enter in the KeyPress handler, and now character classification is getting really tricky in KeyDown.

I did some research: I read about keyboard handling in Petzold s C# book and posted questions on a few lists and newsgroups. What I was advised is that KeyDown isn t a good way to classify characters ”the KeyPress handler is recommended. So I removed the KeyDownHandler and moved all its logic to KeyPress, modifying KeyboardSnapshot and its friends along the way:

 public void XMLKeyPressHandler(object objSender, 
KeyPressEventArgs kea) {
GetText();
model.KeyboardSnapshot(kea);
if ((int) kea.KeyChar == (int) Keys.Enter) {
kea.Handled = true;
if (Control.ModifierKeys == Keys.Shift
this.TestModifierKeys == Keys.Shift )
CallModel(shiftEnterAction);
else if (Control.ModifierKeys == Keys.None)
CallModel(enterAction);
}
else if ( (int) kea.KeyChar == 26 ) { // Control Z
model.Restore();
PutText(textbox, model.LinesArray(), model.SelectionStart);
kea.Handled = true;
}
}
public void KeyboardSnapshot(KeyPressEventArgs kea) {
if ( ! ShouldSnapshot(kea)) return;
if (SimpleInsertCharacter(kea))
this.SnapshotInsertCharacter();
else
this.FullSnapshot();
}
private Boolean ShouldSnapshot(KeyPressEventArgs kea) {
if (kea.KeyChar == (char) 26)
return false;
else
return true;
}
private Boolean SimpleInsertCharacter(KeyPressEventArgs kea) {
if (SelectionLength > 0) return false;
if (Control.ModifierKeys != Keys.None
&& Control.ModifierKeys != Keys.Shift)
return false;
char c = kea.KeyChar;
if ( Char.IsLetterOrDigit(c) Char.IsPunctuation(c))
return true;
else
return false;
}

These were all fairly straightforward changes. I don t like having to refer to Ctrl+Z as 26 , but it was the best I had at the moment. Let s fix that while we re thinking of it:

 public class TextModel : IUndoRestore { 
private static char controlZ = (char) 26;
...
private Boolean ShouldSnapshot(KeyPressEventArgs kea) {
return (! (kea.KeyChar == controlZ ) );
}

That seems a bit nicer. To make this change work, we needed to modify the way our testing works. Recall that we were calling the KeyDown handler directly in the customer tests, so we have to change that to use KeyPress instead:

 private void InterpretCommands(String commands, String message) { 
StringReader reader = new StringReader(commands);
String line = reader.ReadLine();
CreateModel();
while ( line != null) {
if (line.StartsWith("*alt"))
ExecuteMenu("&"+line[4]);
else if (line.StartsWith("*type"))
TypeCharacters(line.Substring(6));
else if (line == "*undo") {
form.XMLKeyPressHandler((object) this, new KeyPressEventArgs( (char) 26 ));
}
else if ( line == "*enter") {
form.XMLKeyPressHandler((object) this, new KeyPressEventArgs ( (char) 13 ));
}
else if ( line == "*shiftEnter") {
form.TestModifierKeys = Keys.Shift;
form.XMLKeyPressHandler((object) this,
new KeyPressEventArgs ( (char) 13 ));
form.TestModifierKeys = Keys.None;
}
else if (line == "*display")
Console.WriteLine("display\r\n{0}\r\nend", model.TestText);
else if (line == "*output")
CompareOutput(reader, message);
else if (line == "*input")
SetInput(reader);
else if (line == "*loadfile")
LoadFile();
else if (line == "*savefile")
SaveFile();
else
Assert(line + " command not defined", false);
line = reader.ReadLine();
}
}

Note those boldface references to TestModifierKeys. Because of the way the KeyPress event works, we don t get modifier bits for the Ctrl key and Shift key and so on as part of the KeyPressEventArgs. Instead, we have to ask Ctrl whether the key is down, during the event. (This is a rather serious flaw in the design of KeyPress events, in my opinion, but we have to deal with it.) So we add a special flag just for testing, TestModifierKeys, that looks like this:

 class XMLNotepad : Form { 
...
private Keys testModifierKeys;
public Keys TestModifierKeys {
get { return testModifierKeys; }
set { testModifierKeys = value; }
}
public void XMLKeyPressHandler(object objSender,
KeyPressEventArgs kea) {
GetText();
model.KeyboardSnapshot(kea);
if ((int) kea.KeyChar == (int) Keys.Enter) {
kea.Handled = true;
if (Control.ModifierKeys == Keys.Shift
this.TestModifierKeys == Keys.Shift )
CallModel(shiftEnterAction);
else if (Control.ModifierKeys == Keys.None)
CallModel(enterAction);
}
else if ( kea.KeyChar == controlZ ) {
model.Restore();
PutText(textbox, model.LinesArray(), model.SelectionStart);
kea.Handled = true;
}
}

TestModifierKeys is, of course, a pure hack to support testing. In view of the importance of this feature and all the trouble I have had with it, I prefer to have the ability to test it, even at this small cost.

Reviewing this code, we see that we have some more literal integers here representing keys, and we ll fix them as we did the Ctrl+Z:

 [TestFixture] public class CustomerTest : Assertion { 
private static char controlZ = (char) 26; private static char enter = (char) 13;
...
else if (line == "*undo") {
form.XMLKeyPressHandler((object) this,
new KeyPressEventArgs( controlZ ));
}
else if ( line == "*enter") {
form.XMLKeyPressHandler((object) this,
new KeyPressEventArgs ( enter ));
}
else if ( line == "*shiftEnter") {
form.TestModifierKeys = Keys.Shift;
form.XMLKeyPressHandler((object) this,
new KeyPressEventArgs ( enter ));
form.TestModifierKeys = Keys.None;
}
...

There are a couple of other references to KeyDown that were changed to KeyPress in just the same way. The change required a bit of book study and less than an hour s programming and writing ”and the final code is simpler than we had when we started.




Extreme Programming Adventures in C#
Javaв„ў EE 5 Tutorial, The (3rd Edition)
ISBN: 735619492
EAN: 2147483647
Year: 2006
Pages: 291

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net