Browsing around, I quickly see why the model wasn t updated. Remember the refactoring that Paul Friedman and I did at the Michigan Union, with the delegates? All the menu items like Insert Pre now work like this:
void MenuInsertPre(object obj, EventArgs ea) {
CallModel(insertPreTagAction);
}
private void CallModel(ModelAction modelAction) {
GetText();
modelAction();
PutText(textbox, model.LinesArray(), model.SelectionStart);
}
public delegate void ModelAction();
private void InitializeDelegates(TextModel model) {
enterAction = new ModelAction(model.Enter);
shiftEnterAction = new ModelAction(model.InsertReturn);
insertSectionAction = new ModelAction(model.InsertSectionTags);
insertPreTagAction = new ModelAction(model.InsertPreTag);
}
ModelAction is a delegate in XMLNotepad each of the menu items calls over to the model to get the action done, while the CallModel() method above makes sure that both sides are updated. We need to code our menu items similarly. Let s try it. I m still living with manual testing here, but I feel it would derail my thought process to write the test. If I had you here with me pairing , maybe you would make me do the right thing. Let s see what happens.
(I ll say I for a while here to emphasize that I m doing this without my pair. I m sure you would have helped me do a better job here. I m starting to regret that promise to show you the bad parts as well as the good.) I added a new ModelAction, named Save Action, initialized it in InitializeDelegates, and called it in the SaveAs method. The results look like this:
class XMLNotepad : Form {
public TestableTextBox textbox;
private TextModel model;
private MenuItem insertPre;
private MenuItem insertSection;
private ModelAction enterAction;
private ModelAction shiftEnterAction;
private ModelAction insertSectionAction;
private ModelAction insertPreTagAction;
private ModelAction saveAction;
private String fileName;
...
private void InitializeDelegates(TextModel model) {
enterAction = new ModelAction(model.Enter);
shiftEnterAction = new ModelAction(model.InsertReturn);
insertSectionAction = new ModelAction(model.InsertSectionTags);
insertPreTagAction = new ModelAction(model.InsertPreTag);
saveAction = new ModelAction(this.SaveFile);
}
void MenuFileSaveAsOnClick(object obj, EventArgs ea) {
SaveFileDialog dialog = new SaveFileDialog();
dialog.Filter = "xml files (*.xml)*.xmlAll files (*.*)*.*";
dialog.FilterIndex = 2 ;
dialog.RestoreDirectory = true ;
if(dialog.ShowDialog() == DialogResult.OK) {
fileName = dialog.FileName;
CallModel(saveAction);
}
else {
MessageBox.Show("File not saved", "XML Notepad");
}
}
void SaveFile() {
using ( StreamWriter writer = File.CreateText(fileName) ) {
model.Save(writer);
}
That makes it work, as I would expect. The CallModel is what does the trick; we always want to use CallModel() to keep the TextBox and the TextModel synchronized. It s back to working, but of course this feature is not tested . I m kind of stressed out because of this unpleasant little surprise. If I try to program right now, I m going to mess up. I m going to take a break instead.