Snapshot Every Time


As an experiment, I d just like to see what happens to performance if we do a Snapshot() operation on every character typed. The KeyDown handler is just the place:

 public void XMLKeyDownHandler(object objSender, KeyEventArgs kea) { 
model.Snapshot();
if (kea.KeyCode == Keys.Enter && kea.Modifiers == Keys.None) {
CallModel(enterAction);
kea.Handled = true;
}
else if (kea.KeyCode == Keys.Enter && kea.Modifiers == Keys.Shift) {
CallModel(shiftEnterAction);
kea.Handled = true;
}
}

This doesn t do anything useful, but it should allow me to run the program and see how it behaves. I ll compile and run the tests first ”they run, as we would expect. Now I ll launch the XML Notepad and play with it. I ll be right back.

I pasted this entire chapter into the XML Notepad and edited it. Performance was just as fast as you could want. I brought up a TaskManager and watched memory usage, which was around 8 megabytes and bounced up in jumps of 100K or more on every keystroke. The good news is that performance is fine, and we re not surprised that it eats memory madly. Now, just for fun, let s implement Undo. We ll have to override the Ctrl+Z handling of the TextBox, which might be tricky. We ll see. I suspect that we will have to work hard to override the Ctrl+Z, so I started with this:

 public void XMLKeyDownHandler(object objSender, KeyEventArgs kea) { 
model.Snapshot();
if (kea.KeyCode == Keys.Enter && kea.Modifiers == Keys.None) {
CallModel(enterAction);
kea.Handled = true;
}
else if (kea.KeyCode == Keys.Enter && kea.Modifiers == Keys.Shift) {
CallModel(shiftEnterAction);
kea.Handled = true;
}
else if (kea.KeyCode == Keys.Z && kea.Modifiers == Keys.Control) { MessageBox.Show("Control Z");
kea.Handled = true;
}
}

Sure enough, the dialog comes up, but the Ctrl+Z is still executed by the TextBox. We ll probably have to capture it in the KeyPress handler as well:

 public void XMLKeyPressHandler(object objSender, KeyPressEventArgs kea) { 
if ((int) kea.KeyChar == (int) Keys.Enter) {
kea.Handled = true;
// this code is here to avoid putting extra enters in the window.
// if removed, when you hit enter, the new <P> line breaks in two:
// <P>
// </P> like that.
}
else if ((int) kea.KeyChar == 26) { MessageBox.Show("KeyPress Control Z");
kea.Handled = true;
}
}

This triggers the dialog and does not do the delete. I ll try putting a model.Restore here to see what happens:

 public void XMLKeyPressHandler(object objSender, KeyPressEventArgs kea) { 
if ((int) kea.KeyChar == (int) Keys.Enter) {
kea.Handled = true;
// this code is here to avoid putting extra enters in the window.
// if removed, when you hit enter, the new <P> line breaks in two:
// <P>
// </P> like that.
}
else if ((int) kea.KeyChar == 26) {
model.Restore();
kea.Handled = true;
}
}

In fact, nothing happens. My guess is that we have done a Snapshot() already, so the Restore() isn t doing anything. I ll try two copies of Restore() to see what happens. Nothing, and the reason becomes clear. It isn t enough to Restore the TextModel; we need to reload the TextBox from it. I ll hack that in ”and it still doesn t work. I m going to resort to a few debugging statements to print the depth of the snapshot stack.

Lesson  

Debugging statements are evil. Even more evil is actually using the debugger and probing around, but we haven t fallen that low yet. Both these practices are signs that we do not know what is going on. The question is, who is to be the master, you or your program. A better course here would have been to write some tests aimed at learning or to make the flow of the system more obvious. In the case in hand, I was on a quest to see whether this idea would work, and if it gets a bit inefficient here, we re back on track fairly quickly.

It occurs to me as I write this that we don t have a Customer Acceptance Test for Undo. I ve been a bit erratic on that, because I am the customer and I know what I want, but I would be serving as a better example if I had more customer tests.

 public void Snapshot() { 
SavedModels.Push(new TextModel(this));
Console.WriteLine("push {0}", SavedModels.Count);
}
public void Restore() {
Console.WriteLine("prepop {0}", SavedModels.Count);
TextModel savedModel = (TextModel) SavedModels.Pop();
Console.WriteLine("postpop {0}", SavedModels.Count);
lines = savedModel.Lines;
selectionStart = savedModel.SelectionStart;
}

This immediately shows me what s happening: the Ctrl key is repeating, and the SnapShot() is being done on every repeat. If I hold down Ctrl+Z long enough, the Notepad will actually undo and then it blows up, probably because it isn t protected yet against too many pop operations. So I ll need to see what I can do about the Ctrl key.

Lesson  

In spite of the fact that this is taking a bit of work, I d like to point out that it is work that we could not have saved by designing the Undo capability sooner. We just need to learn more about how the key- pressing logic works, and that had to be done sooner or later. I ll work on that now.

In addition, in my own defense, it s hard to see how we could have written a test that discovered that Ctrl was repeating any sooner than by putting in the trace. I still think it would be better to have done the test, but it is my practice to forgive myself when I don t do something because I don t have a clue how to do it!

After a little fiddling, here s what the two handlers look like, with the changed and interesting bits highlighted:

 public void XMLKeyDownHandler(object objSender, KeyEventArgs kea) { 
if (kea.KeyCode != Keys.ControlKey &&
kea.KeyCode != Keys.Alt &&
kea.KeyCode != Keys.ShiftKey)
model.Snapshot();
if (kea.KeyCode == Keys.Enter && kea.Modifiers == Keys.None) {
CallModel(enterAction);
kea.Handled = true;
}
else if (kea.KeyCode == Keys.Enter && kea.Modifiers == Keys.Shift) {
CallModel(shiftEnterAction);
kea.Handled = true;
}
else if (kea.KeyCode == Keys.Z && kea.Modifiers == Keys.Control) { Console.WriteLine("keydown z");
kea.Handled = true;
}
}

public void XMLKeyPressHandler(object objSender, KeyPressEventArgs kea) {
if ((int) kea.KeyChar == (int) Keys.Enter) {
kea.Handled = true;
// this code is here to avoid putting extra enters in the window.
// if removed, when you hit enter, the new <P> line breaks in two:
// <P>
// </P> like that.
}
else if ((int) kea.KeyChar == 26) {
model.Restore();
model.Restore(); PutText(textbox, model.LinesArray(), model.SelectionStart); kea.Handled = true;
}
}

This is nearly working. I just noticed the two Restore operations are still there, and I suspect they re necessary. The other oddity is that when we type text in between tags and then issue a Ctrl+Z, all the text disappears on the first Ctrl+Z, and then nothing happens while we type enough more Ctrl+Zs to have deleted the other characters. The reason is that in the KeyDown handler, we re taking a snapshot on every character, but the model isn t updated, so it doesn t know that the characters are there.

But this was just an experiment to see what would happen. What happened is that we have in fact built a rudimentary but operating Undo capability. I ll take a well-deserved break now and then buckle down to putting in the real code.




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