While opening files is a function of the parent form, saving files is a function of the child forms. Typically, applications have both a Save and a Save As command. The Save command simply replaces an existing copy of a named document with a new copy; the Save As command presents a dialog box to let the user change a document’s filename before saving it.
Complicating matters slightly, if the user slects the Save command for a document that has never been named or saved, the application should treat it as if the user had selected the Save As command in order to let him select a filename and location.
Before writing the code that will take care of saving and naming the files, let’s see how the TextSaved property, which lets us know if a document needs to be saved, is modified by the program as files are changed.
When we were setting up the child form template earlier in this chapter, we created a Boolean TextSaved property. Its job is to report whether the text contained in the child form’s text box has been saved (or if it is in need of saving). The initial value of TextSaved is True, indicating that the file has not been modified since it was last saved. However, we need to set it to False whenever any text in the text box changes to indicate that the document needs saving. This is a simple matter of adding code to the txtMain text box’s TextChanged event handler, which is invoked whenever the text in the text box changes (usually by user interaction):
1. | |
2. |
As you will see, the functionality that actually writes a file to disk will need to be called from several locations; therefore, it makes sense to create a standalone SaveFile procedure in the child form class. As with the parent form, the child form’s Code window needs to include a reference to the System.IO namespace, so enter Imports System.IO at the very top of frmChild’s Code window, just above the Public Class frmChild. Then, enter the following code at the end in the Code window for the child form, just above the End Class line:
Private Sub SaveFile() Dim MyFile As StreamWriter If sFileName = "" Then SaveFileAs() End If MyFile = File.CreateText(sFileName) MyFile.Write(txtMain.Text) MyFile.Close() Me.TextSaved = True End Sub
If the SaveFile procedure detects that the form’s FileName property is empty, it calls the SaveFileAs procedure, which will cause the user to be prompted for a filename and location. Notice that the SaveFile procedure sets the form’s TextSaved property to True, indicating, that the text contained in the form’s text box has been saved and is unmodified since then.
Next, we are going to create a standalone Save As procedure that may seem a little unnecessary at first, but its purpose will become clear soon enough. The Save As procedure will simply invoke the SaveFileDialog control (dlgSave) that we created when we set up the form, using the current contents of the form’s FileName property to pre-populate the dialog box’s FileName property. Enter the following procedure in the Code window just after the SaveFile procedure:
Sub SaveFileAs() dlgSave.FileName = Me.FileName dlgSave.ShowDialog() End Sub
When the user selects Save from the File menu, the program must determine if the user has named the file yet (as determined by examining the value of the internal sFileName variable). If so, then the SaveFile procedure is to be invoked; however, if the document is unnamed, the SaveFileAs procedure must be invoked instead, so that the user will have an opportunity to name the file. Enter the following code into the Click event handler for the mnuFileSave menu item:
Private Sub mnuFileSave_Click(ByVal sender As Object, _ ByVal e As System.EventArgs) Handles mnuFileSave.Click If sFileName > "" Then SaveFile() Else SaveFileAs() End If End Sub
When the user selects Save As from the File menu, she is specifically saying that she wants to be presented with the opportunity to specify a new name for the file. The following code, which is to be entered into the Click event handler for the mnuFileSaveAs menu item, will accomplish this by invoking the SaveFileAs procedure we just created (which in turn shows the SaveFileDialog box):
Private Sub mnuFileSaveAs_Click(ByVal sender As Object, _ ByVal e As System.EventArgs) Handles mnuFileSaveAs.Click SaveFileAs() End Sub
When the user is presented with the SaveFileDialog control and clicks OK, he has specified a new filename under which the document is to be saved. The following code, entered into the FileOK event handler of dlgSave, checks the value of the argument that was passed into the procedure, just to make sure the user hasn’t cancelled his request or left out the filename. If he has not, the form’s FileName property is updated with the dialog box’s FileName property, and the SaveFile procedure is invoked to actually save the file:
Private Sub dlgSave_FileOk(ByVal sender As Object, _ ByVal e As System.ComponentModel.CancelEventArgs) Handles dlgSave.FileOk If e.Cancel = False Then Me.FileName = dlgSave.FileName SaveFile() End If End Sub
When a user closes a child window, the program should check the TextSaved property to see if the document contained in that instance of the child window has been modified since it was last saved. If it has, the user should be given the option of saving the document before the window closes. To do this, we will add code to the child form’s Closing event handler, which is invoked after the user attempts to close the window, but before the window is actually closed. Follow these steps:
1. | |
2. |
Enter the following code to complete the frmChild_Closing event handler:
Private Sub frmChild_Closing(ByVal sender As Object, _ ByVal e As System.ComponentModel.CancelEventArgs) Handles MyBase.Closing If Me.TextSaved = False Then Dim sMsg As String Dim nResult As Integer sMsg = "Save changes to " If Me.FileName = "" Then sMsg += "this untitled document" Else sMsg += Mid(sFileName, InStrRev(sFileName, "") + 1) End If sMsg += " before closing it?" nResult = MessageBox.Show(sMsg, "System Message", _ MessageBoxButtons.YesNoCancel) Select Case nResult Case DialogResult.Cancel e.Cancel = True Case DialogResult.Yes SaveFile() Case Else 'Do nothing; let the window close. End Select End If End Sub |
This code builds a String variable named sMsg to contain a message asking the user if he wants to save the file contained in the child document window. It then displays that message as it invokes a message box, using the Yes/No/Cancel set of buttons. A Select Case statement evaluates the user’s response. If he selects Yes, the SaveFile procedure is called (which will, in turn, call SaveFileAs if necessary); if he selects Cancel, the Cancel property of the procedure’s arguments is set to True, which aborts the closing of the window; otherwise, the user selects No and the closing of the window can continue.
When you created the menu system for the child form, you included a Close option under the File menu. This option should serve the same purpose as if the user clicked the window’s Close button at the upper-right. The code for this is simple; place it in the Click event handler for the mnuFileClose menu item:
Me.Close()
This would be a good time to save your project and test the file opening and saving functionality. Run the program and create some documents, save them, change their names by using Save As, try to close documents that have not been saved, and so on.
3.133.138.177