File Input and Output
AAHC

Click on A to make all fonts on the page smaller.

Click on A to make all fonts on the page larger.

Click on HC to toggle high contrast mode. When you move your mouse over some bold words in high contrast mode, related words are automatically highlighted. Text is shown in black and white.


A major use of programs is accessing files: seeing if a file exists, creating and deleting files, reading from and writing to files. Previously, we've used the System.IO.FileInfo class to retrieve information about a file (it will also work for a directory). We will need to use this object again to see if a file exists. We'll add a few new classes to enable us to work with files.

Files that we humans can read or write are called text (plain text, ASCII) files; or binary files. Files that can only be read by a computer are called binary files. Files that contain formatting information, like word-processing documents, typically are binary. All files, actually, are binary files, as all data stored on a computer can be reduced to the binary zeros and ones the computer reads, and we can read text files with the same C# classes that handle binary data. However, we have other classes we can use with text files. Let's explore each by extending our FormsIO project.

Note You might wonder if we'll return to a Console Application for files. We won't, because file handling works the same whether you have a Console Application or a Windows Forms Application. Of course, you can't use some of the nicer Windows GUI controls with a Console Application, so we'll stick with a Windows Forms Application for this project.

Using File Object

Modify the FormsIO form as shown below by adding the controls listed. Change each control Name property to match the Name Property column, and each Text property to match the Text Property column. Set the Multiline property of the contentTextBox control to True so you can expand it vertically. Double-click on each of the three buttons to add event handlers.

ObjectName PropertyText Property
TextBoxcontentTextBox
CheckBoxbinaryCheckBoxBinary
ButtonsaveButtonSave
ButtonopenButtonOpen
ButtonexitButtonExit

Next, let's add dialog box controls from the Toolbox that will assist us in saving and opening files. Note that you can drag these anywhere on the form, and they'll appear under the form.

From the Dialogs section of the Toolbox, drag a SaveFileDialog onto the FormsIO form. Change the Name property to saveFileDialog. Drag an OpenFileDialog onto the form. Change the Name property to openFileDialog:

Next, we'll add code to bring up the dialog controls when the user clicks on the buttons, code to exit the application, and a couple of C# constants we'll use with the dialogs.

Select the FormsIO.cs Form Code Editor tab, and modify the code as shown:

FormsIO.cs
using ...

namespace FormsIO
{
    public partial class FormsIO : Form
    {
     private const string TextFilters = "txt files (*.txt)|*.txt"; 
        private const string BinaryFilters = "All files (*.*)|*.*";
        
        public FormsIO()
        {
            InitializeComponent();
        }
        
        private void dialogButton_Click(object sender, EventArgs e)
        {
            Popup myPopup = new Popup();
            
            if (myPopup.ShowDialog() == DialogResult.OK)
            {
                hiLabel.Text = "Hi, " + myPopup.UserName;
            }
            else
            {
                hiLabel.Text = "So don't tell me your name.";
            }
        }
        
        private void saveButton_Click(object sender, EventArgs e)
        {
            saveFileDialog.InitialDirectory = Application.StartupPath;
            saveFileDialog.Filter = binaryCheckBox.Checked ? BinaryFilters : TextFilters;
            saveFileDialog.FilterIndex = 1;
            saveFileDialog.RestoreDirectory = true;
            saveFileDialog.FileName = "";
            saveFileDialog.Title = "Save File";
            
            if (saveFileDialog.ShowDialog() == DialogResult.OK)
            {
                // Save the file.
            
            }
        }
        
        private void openButton_Click(object sender, EventArgs e)
        {
            openFileDialog.InitialDirectory = Application.StartupPath;
            openFileDialog.Filter = binaryCheckBox.Checked ? BinaryFilters : TextFilters;
            openFileDialog.FilterIndex = 1;
            openFileDialog.RestoreDirectory = true;
            openFileDialog.FileName = "";
            openFileDialog.Title = "Open File";
            openFileDialog.Multiselect = false;
            
            if (openFileDialog.ShowDialog() == DialogResult.OK)
            {
                // Open the file.
            
            }
        }
        
        private void exitButton_Click(object sender, EventArgs e)
        {
            this.Close();
        }
    }
}

and to run the program. We need to create a folder for the files we'll be using, so click the Save button on the form. In the Save File dialog that appears, select the folder with your user name (in place of "smiller" in the screen shot) under My Computer, and then click New folder:

Enter the folder name csharp2 and press Enter:

We don't have code in place yet to save a file, but you can see that C# saves us a lot of work in providing the dialog for saving, and we now have a working folder for the rest of the lesson. So click Cancel, and then Exit. Let's discuss the new code.

OBSERVE: FormsIO.cs
.
.
.
    public partial class FormsIO : Form
    {
    private const string TextFilters = "txt files (*.txt)|*.txt";
        private const string BinaryFilters = "All files (*.*)|*.*";
.
.
.        
        private void saveButton_Click(object sender, EventArgs e)
        {
            saveFileDialog.InitialDirectory = Application.StartupPath;
            saveFileDialog.Filter = binaryCheckBox.Checked ? BinaryFilters : TextFilters;
            saveFileDialog.FilterIndex = 1;
            saveFileDialog.RestoreDirectory = true;
            saveFileDialog.FileName = "";
            saveFileDialog.Title = "Save File";
            
            if (saveFileDialog.ShowDialog() == DialogResult.OK)
            {
                // Save the file.
            
            }
        }
        
        private void openButton_Click(object sender, EventArgs e)
        {
            openFileDialog.InitialDirectory = Application.StartupPath;
            openFileDialog.Filter = binaryCheckBox.Checked ? BinaryFilters : TextFilters;
            openFileDialog.FilterIndex = 1;
            openFileDialog.RestoreDirectory = true;
            openFileDialog.FileName = "";
            openFileDialog.Title = "Open File";
            openFileDialog.Multiselect = false;
            
            if (openFileDialog.ShowDialog() == DialogResult.OK)
            {
                // Open the file.
            
            }
        }
.
.
.

The added code implements the setup and display of the saveFileDialog and openFileDialog dialog boxes. We use the Application.StartupPath variable that contains the full application path as the default folder for the dialog boxes. We also use, again, the ternary/conditional operator to set the dialog filters. The dialog filters determine what is available in the Files To List combo box for each of the dialog boxes. We also test to make sure that we only process either dialog if the Open or Save button is clicked, which is the equivalent of an OK button, by testing the result of calling the ShowDialog() method against DialogResult.OK. Now let's add the code to open or save the text within the contentTextBox.

Select the FormsIO.cs form Code Editor tab, and modify the code as shown below (we'll again just show the relevant areas, as our code is getting large).

FormsIO.cs
using ...
using System.IO 
.
.
.
        private void saveButton_Click(object sender, EventArgs e)
        {
            saveFileDialog.InitialDirectory = Application.StartupPath;
            saveFileDialog.Filter = binaryCheckBox.Checked ? BinaryFilters : TextFilters;
            saveFileDialog.FilterIndex = 1;
            saveFileDialog.RestoreDirectory = true;
            saveFileDialog.FileName = "";
            saveFileDialog.Title = "Save File";
            
            if (saveFileDialog.ShowDialog() == DialogResult.OK)
            {
                // Save the file.
                if (saveFileDialog.FileName.Length > 0)
                {
                    if (!binaryCheckBox.Checked)
                    {
                        File.WriteAllText(saveFileDialog.FileName, contentTextBox.Text);
                    }
                    else
                    {
                        UnicodeEncoding encoding = new UnicodeEncoding();
                        File.WriteAllBytes(saveFileDialog.FileName, encoding.GetBytes(contentTextBox.Text));
                    }
                    MessageBox.Show(Path.GetFileName(saveFileDialog.FileName) + " saved", this.Text, 
                            MessageBoxButtons.OK, MessageBoxIcon.Information);
                }
            }
        }
        
        private void openButton_Click(object sender, EventArgs e)
        {
            openFileDialog.InitialDirectory = Application.StartupPath;
            openFileDialog.Filter = binaryCheckBox.Checked ? BinaryFilters : TextFilters;
            openFileDialog.FilterIndex = 1;
            openFileDialog.RestoreDirectory = true;
            openFileDialog.FileName = "";
            openFileDialog.Title = "Open File";
            openFileDialog.Multiselect = false;
            
            if (openFileDialog.ShowDialog() == DialogResult.OK)
            {
                // Open the file.
                if (openFileDialog.FileName.Length > 0)
                {
                    if (!binaryCheckBox.Checked)
                    {
                        contentTextBox.Text = File.ReadAllText(openFileDialog.FileName);
                    }
                    else
                    {
                        UnicodeEncoding encoding = new UnicodeEncoding();
                        contentTextBox.Text = encoding.GetString(File.ReadAllBytes(openFileDialog.FileName));
                    }
                    MessageBox.Show(Path.GetFileName(openFileDialog.FileName) + " loaded", this.Text, 
                            MessageBoxButtons.OK, MessageBoxIcon.Information);
                }
            }
        }
.
.
.

and to run the program. Enter some text in the contentTextBox and click Save; in the Save File dialog that appears, navigate to the csharp2 folder we created (under Computer | username); enter the file name MyTextFile.txt, and click Save. You'll see a confirmation that the file was saved. Now, enter different text, check the Binary box, and click Save again; in the Save File dialog, again navigate to the csharp2 folder, enter the file name MyBinaryFile.bin, and click Save. Again you'll see the confirmation. You can use the Open button now to open either of the files—note that when Binary is checked, you see both files, and when it isn't checked, you only see the .txt file.

Let's discuss how this code works.

OBSERVE: FormsIO.cs
using...
using System.IO;
.
.
.
        private void saveButton_Click(object sender, EventArgs e)
        {
            saveFileDialog.InitialDirectory = Application.StartupPath;
            saveFileDialog.Filter = binaryCheckBox.Checked ? BinaryFilters : TextFilters;
            saveFileDialog.FilterIndex = 1;
            saveFileDialog.RestoreDirectory = true;
            saveFileDialog.FileName = "";
            saveFileDialog.Title = "Save File";
            
            if (saveFileDialog.ShowDialog() == DialogResult.OK)
            {
                // Save the file.
                if (saveFileDialog.FileName.Length > 0)
                {
                    if (!binaryCheckBox.Checked)
                    {
                        File.WriteAllText(saveFileDialog.FileName, contentTextBox.Text);
                    }
                    else
                    {
                        UnicodeEncoding encoding = new UnicodeEncoding();
                        File.WriteAllBytes(saveFileDialog.FileName, encoding.GetBytes(contentTextBox.Text));
                    }
                    MessageBox.Show(Path.GetFileName(saveFileDialog.FileName>) + " saved", this.Text, 
                        MessageBoxButtons.OK, MessageBoxIcon.Information);
                }
            }
        }
.
.
.

The code is similar for both dialogs. First, We make sure a file was specified by checking that the length of FileName is greater than zero. If Binary is not checked, we save the contents of the contentTextBox to the file using the File.WriteAllText method. If Binary is checked, we use create an instance of the UnicodeEncoding class and use File.WriteAllBytes to write the contents of the TextBox as binary data. We use the MessageBox object to notify the user that the file was either saved or loaded. We also use the Path.GetFileName method to extract just the file name component of the FileName dialog box property (which contains the complete path and filename).

Try changing the notification so that it gives the full path and filename.

Cool stuff! Start thinking about other ways you could use this kind of functionality.

Before moving on to the next topic, we want to point you to a fantastic web page resource by Microsoft that lists links to C# tips, solutions, and examples to most common input/output (I/O) tasks: Common I/O Tasks. Review the list of topics to get an idea of the types of issues included.

Using Streams

Although using the File object works well, the general concept of reading and writing data can apply to many other scenarios beyond just a file; for example, to and from a network, to and from an object, and to and from a database. Potentially, you could envision other File classes for each of these data sources, but they all share common functionality related to data input and output. To encompass that commonality, programming languages evolved to include the concept of a stream. Using streams, we can simplify our coding to use similar methods even though the data sources differ. Let's rewrite our previous code using streams.

Select the FormsIO form Code Editor tab, and modify the openButton_Click() method as shown below.

FormsIO.cs: openButton_Click method
private void openButton_Click(object sender, EventArgs e)
{
    openFileDialog.InitialDirectory = Application.StartupPath;
    openFileDialog.Filter = binaryCheckBox.Checked ? BinaryFilters : TextFilters;
    openFileDialog.FilterIndex = 1;
    openFileDialog.RestoreDirectory = true;
    openFileDialog.FileName = "";
    openFileDialog.Title = "Open File";
    openFileDialog.Multiselect = false;
    
    if (openFileDialog.ShowDialog() == DialogResult.OK)
    {
        // Open the file.
        if (openFileDialog.FileName.Length > 0)
        {
            if (!binaryCheckBox.Checked)
            {
                contentTextBox.Text = File.ReadAllText(openFileDialog.FileName);
                // Create a stream connected to a file.
                FileStream fileStream = File.Open(openFileDialog.FileName, FileMode.Open);
                // Create a text stream reader connected to the file stream.
                StreamReader streamReader = new StreamReader(fileStream);
                // Read the data to the end of the stream.
                contentTextBox.Text = streamReader.ReadToEnd();
                // Close the text stream reader.
                streamReader.Close();
                // Close the file stream.
                fileStream.Close();
            }
            else
            {
                UnicodeEncoding encoding = new UnicodeEncoding();
                contentTextBox.Text = encoding.GetString(File.ReadAllBytes(openFileDialog.FileName));
                // Create a stream connected to a file.
                FileStream fileStream = File.Open(openFileDialog.FileName, FileMode.Open);
                // Create a binary stream reader connected to the file stream.
                BinaryReader binaryReader = new BinaryReader(fileStream);
                // Get the length of the file in bytes.
                long fileLength = new FileInfo(openFileDialog.FileName).Length;
                // Create a byte array to hold the byte data.
                byte[] byteArray = new byte[fileLength];
                // Read the specified amount of data from the binary stream.
                byteArray = binaryReader.ReadBytes((int)fileLength);
                // Prepare an encoding object for translating the data read from the file.
                UTF8Encoding encoding = new UTF8Encoding();
                // Convert the byte data into a format we can read.
                contentTextBox.Text = encoding.GetString(byteArray);
                // Close the binary stream reader.
                binaryReader.Close();
                // Close the file stream.
                fileStream.Close();
            }
            MessageBox.Show(Path.GetFileName(openFileDialog.FileName) + " loaded", this.Text,
            MessageBoxButtons.OK, MessageBoxIcon.Information);
        }
    }
}

and to run the program. It should work exactly as before.

Let's discuss the changes.

OBSERVE: FormsIO.cs openButton_Click method
.
.
.
            if (!binaryCheckBox.Checked)
            {
                // Create a stream connected to a file.
                FileStream fileStream = File.Open(openFileDialog.FileName, FileMode.Open);
                // Create a text stream reader connected to the file stream.
                StreamReader streamReader = new StreamReader(fileStream);
                // Read the data to the end of the stream.
                contentTextBox.Text = streamReader.ReadToEnd();
                // Close the text stream reader.
                streamReader.Close();
                // Close the file stream.
                fileStream.Close();
            }
            else
            {
                // Create a stream connected to a file.
                FileStream fileStream = File.Open(openFileDialog.FileName, FileMode.Open);
                // Create a binary stream reader connected to the file stream.
                BinaryReader binaryReader = new BinaryReader(fileStream);
                // Get the length of the file in bytes.
                long fileLength = new FileInfo(openFileDialog.FileName).Length;
                // Create a byte array to hold the byte data.
                byte[] byteArray = new byte[fileLength];
                // Read the specified amount of data from the binary stream.
                byteArray = binaryReader.ReadBytes((int)fileLength);
                // Prepare an encoding object for translating the data read from the file.
                UTF8Encoding encoding = new UTF8Encoding();

                                // Convert the byte data into a format we can read.
                contentTextBox.Text = encoding.GetString(byteArray);
                // Close the binary stream reader.
                binaryReader.Close();
                // Close the file stream.
                fileStream.Close();
            }
.
.
.

Don't be overwhelmed! Yes, we've added quite a few changes to the openButton_Click method (we also added copious single-line comments to help!), but we'll break this up into small chunks to explain them all! The general steps are:

  1. Create a stream connected to a source.
  2. Create a stream reader object.
  3. Connect stream reader to our stream source.
  4. Read as much of the data from the stream we want.
  5. For binary: determine how to display data as necessary.
  6. Close stream reader connection.
  7. Close stream source connection.

Now, line by line in the code:

OBSERVE:
// Create a stream connected to a file.
FileStream fileStream = File.Open(openFileDialog.FileName, FileMode.Open);

For both branches of the if statement, we use the same code (above), which creates a fileStream object variable of type FileStream, using the Open method of the File object. Notice that when we use the Open method, we have to specify the access mode. For our needs, we use the FileMode.Open mode, assuming the file should already exist because we're using the OpenFileDialog object.

NoteRemember, you can use Intellisense to help you in learning more about objects and types in Studio by typing the object type or variable name, then the period, and waiting for Intellisense to give you a dropdown list of the options. You can use the arrow keys to move through the options, and pause again, waiting for Intellisense to give you an additional popup of information about the highlighted option. Below are the different FileMode access modes, with the Open mode highlighted.

OBSERVE:
// Create a text stream reader connected to the file stream.
StreamReader streamReader = new StreamReader(fileStream);
// Read the data to the end of the stream.
contentTextBox.Text = streamReader.ReadToEnd();

Next for text files, we create a stream reader object to enable reading data from a stream, and connect our stream reader to the stream source (a file, in our example). We use a StreamReader object for reading our stream data as text. Then, we read the data using the ReadToEnd method of the streamReader object.

OBSERVE:
// Create a binary stream reader connected to the file stream.
BinaryReader binaryReader = new BinaryReader(fileStream);
// Get the length of the file in bytes.
long fileLength = new FileInfo(openFileDialog.FileName).Length;
// Create a byte array to hold the byte data.
byte[] byteArray = new byte[fileLength];
// Read the specified amount of data from the binary stream.
byteArray = binaryReader.ReadBytes((int)fileLength);

For binary data, we use a BinaryReader object. The method differs for reading text and binary data, as binary data by definition may not be printable, but the non-printable nature is not the only difference: text data, as read, has a meaning, such as strings, but binary data is typically read as bytes, and can be interpreted as string, numerical data, or images, encrypted information, etc. So, for binary, we need to read the data in as byte data. We store the data in an object called an array (we'll discuss arrays later) using the variable byteArray. For now, just understand that the byteArray contains the characters you typed in the TextBox, translated one by one to computer language; for example, if you typed "now is the time," the byteArray would contain a table of decimal values for n, o, w, space, i, s, space, etc. We need to know how many of these bytes to read, and to size the array appropriately, so we use the Length property of the FileInfo object to set the variable fileLength. We use the ReadBytes method of the binaryReader object to read the data.

NoteYou may note that we used (int) to convert the fileLength variable from a long to an int. Why? The FileInfo Length property returns a long, but the BinaryReader ReadBytes method requires an int. The fact that the FileInfo Length method returns a long, and the BinaryReader ReadBytes method uses an int, indicates that the ReadBytes method is not capable of reading very large data streams. Reading a large amount of data is a trade-off between efficiency and performance. The more data you can read at once is general more efficient, but very large amounts might cause your program to slow down during the read process. You'll find that frequently reading from a stream is an iterative process.

OBSERVE:
// Prepare an encoding object for translating the data read from the file.
UTF8Encoding encoding = new UTF8Encoding();

We next see that we create another encoding object, but rather than Unicode, this time we create one of type UTF8Encoding. Actually, UTF8 is Unicode, but a specific version that allows for backward compatibility with ASCII. (In a later lesson, we'll get more into character sets and encoding.) We use the UTF8 encoding object to convert the individual bytes in the byteArray into string data we can read.

OBSERVE:
// Close the text stream reader.
streamReader.Close();
// Close the file stream.
fileStream.Close();
OBSERVE:
// Close the binary stream reader.
binaryReader.Close();
// Close the file stream.
fileStream.Close();

Finally, for both types of file, we close out our stream readers, streamReader and binaryReader, and stream objects fileStream, by calling the appropriate Close() method. When opening stream objects and readers, always be careful to close them when you finish using them; typically, you should close them as soon as you can. Opening streams can place locks on the stream sources, and you want to make sure you don't tie up resources. In fact, closing these objects is so important that C# includes a syntax, using, that ensures that limited-resource objects you create have limited scope. Once these objects go out of scope, their Close() methods are automatically called. Let's rewrite our code to take advantage of this syntax. We'll also use the FileStream object inline, which will negate the need for declaring the fileStream variable, and eliminate the byteArray variable.

Modify the FormsIO.cs openButton_Click method as shown below.

FormsIO.cs: openButton_Click method
private void openButton_Click(object sender, EventArgs e)
{
    openFileDialog.InitialDirectory = Application.StartupPath;
    openFileDialog.Filter = binaryCheckBox.Checked ? BinaryFilters : TextFilters;
    openFileDialog.FilterIndex = 1;
    openFileDialog.RestoreDirectory = true;
    openFileDialog.FileName = "";
    openFileDialog.Title = "Open File";
    openFileDialog.Multiselect = false;
    
    if (openFileDialog.ShowDialog() == DialogResult.OK)
    {
        // Open the file.
        if (openFileDialog.FileName.Length > 0)
        {
            if (!binaryCheckBox.Checked)
            {
                // Create a stream connected to a file.
                FileStream fileStream = File.Open(openFileDialog.FileName, FileMode.Open);
                // Create a text stream reader connected to the file stream.
                StreamReader streamReader = new StreamReader(fileStream);
                using (StreamReader streamReader = new StreamReader(File.Open(openFileDialog.FileName, FileMode.Open)))
                {
                    // Read the data to the end of the stream.
                    contentTextBox.Text = streamReader.ReadToEnd();
                }
                // Close the text stream reader.
                streamReader.Close();
                // Close the file stream.
                fileStream.Close();
            }
            else
            {
                // Create a stream connected to a file.
                FileStream fileStream = File.Open(openFileDialog.FileName, FileMode.Open);
                // Create a binary stream reader connected to the file stream.
                using (BinaryReader binaryReader = new BinaryReader(File.Open(openFileDialog.FileName, FileMode.Open)))
                {
                    // Get the length of the file in bytes.
                    long fileLength = new FileInfo(openFileDialog.FileName).Length;
                    // Create a byte array to hold the byte data.
                    byte[] byteArray = new byte[fileLength];
                    // Read the specified amount of data from the binary stream.
                    byteArray = binaryReader.ReadBytes((int)fileLength);
                    // Prepare an encoding object for translating the data read from the file.
                    UTF8Encoding encoding = new UTF8Encoding();
                    // Convert the byte data into a format we can read.
                    contentTextBox.Text = encoding.GetString(byteArray);
                    contentTextBox.Text = encoding.GetString(binaryReader.ReadBytes((int)fileLength));
                }
                // Close the binary stream reader.
                binaryReader.Close();
                // Close the file stream.
                fileStream.Close();
            }
            MessageBox.Show(Path.GetFileName(openFileDialog.FileName) + " loaded", this.Text,
            MessageBoxButtons.OK, MessageBoxIcon.Information);
        }
    }
}

and to run the program. It should work just like before.

Let's discuss the new code.

OBSERVE: FormsIO.cs openButton_Click method
private void openButton_Click(object sender, EventArgs e)
{
    openFileDialog.InitialDirectory = Application.StartupPath;
    openFileDialog.Filter = binaryCheckBox.Checked ? BinaryFilters : TextFilters;
    openFileDialog.FilterIndex = 1;
    openFileDialog.RestoreDirectory = true;
    openFileDialog.FileName = "";
    openFileDialog.Title = "Open File";
    openFileDialog.Multiselect = false;
    
    if (openFileDialog.ShowDialog() == DialogResult.OK)
    {
        // Open the file.
        if (openFileDialog.FileName.Length > 0)
        {
            if (!binaryCheckBox.Checked)
            {
                // Create a text stream reader connected to the file stream.
                using (StreamReader streamReader = new StreamReader(File.Open(openFileDialog.FileName, FileMode.Open)))
                {
                    // Read the data to the end of the stream.
                    contentTextBox.Text = streamReader.ReadToEnd();
                }
            }
            else
            {
                // Create a binary stream reader connected to the file stream.
                using (BinaryReader binaryReader = new BinaryReader(File.Open(openFileDialog.FileName, FileMode.Open)))
                {
                    // Get the length of the file in bytes.
                    long fileLength = new FileInfo(openFileDialog.FileName).Length;
                    // Prepare an encoding object for translating the data read from the file.
                    UTF8Encoding encoding = new UTF8Encoding();
                    // Convert the byte data into a format we can read.
                    contentTextBox.Text = encoding.GetString(binaryReader.ReadBytes((int)fileLength));
                }
            }
            MessageBox.Show(Path.GetFileName(openFileDialog.FileName) + " loaded", this.Text,
            MessageBoxButtons.OK, MessageBoxIcon.Information);
        }
    }
}

The using syntax allows us to remove the Close() method calls. We refactored some of our code to make it more concise and eliminate unneeded variables. Refactoring code is a learned process and is very dependent on the programmer's preferences. While concise code is often preferred, you may sometimes trade the conciseness for readability. Also, you might think that because the variables aren't declared they don't exist, but each former call that had a variable still has the need to store the results of the method calls somewhere, and C# creates and disposes of temporary variables for the brief time they're needed. If you don't need to reuse the information in a variable, a common practice is to not declare them, but the need for storage for the data is still present.

To complete this section, let's make the necessary changes to save our data using streams.

Modify the FormsIO saveButton_Click method as shown below.

FormsIO.cs: saveButton_Click method
private void saveButton_Click(object sender, EventArgs e)
{
    saveFileDialog.InitialDirectory = Application.StartupPath;
    saveFileDialog.Filter = binaryCheckBox.Checked ? BinaryFilters : TextFilters;
    saveFileDialog.FilterIndex = 1;
    saveFileDialog.RestoreDirectory = true;
    saveFileDialog.FileName = "";
    saveFileDialog.Title = "Save File";
    
    if (saveFileDialog.ShowDialog() == DialogResult.OK)
    {
        // Save the file.
        if (saveFileDialog.FileName.Length > 0)
        {
            if (!binaryCheckBox.Checked)
            {
                File.WriteAllText(saveFileDialog.FileName, contentTextBox.Text);
                using (StreamWriter streamWriter = new StreamWriter(File.Open(saveFileDialog.FileName, FileMode.OpenOrCreate)))
                {
                    streamWriter.Write(contentTextBox.Text);
                }
            }
            else
            {
                UnicodeEncoding encoding = new UnicodeEncoding();
                File.WriteAllBytes(saveFileDialog.FileName, encoding.GetBytes(contentTextBox.Text));
                using (BinaryWriter binaryWriter = new BinaryWriter(File.Open(saveFileDialog.FileName, FileMode.OpenOrCreate)))
                {
                    binaryWriter.Write(encoding.GetBytes(contentTextBox.Text));
                }
            }
            MessageBox.Show(Path.GetFileName(saveFileDialog.FileName) + " saved", this.Text, 
            MessageBoxButtons.OK, MessageBoxIcon.Information);
        }
    }
}
            

and to run the program. Again, it should work just like before.

Let's discuss the new code.

OBSERVE: FormsIO.cs saveButton_Click method
.
.
.
            if (!binaryCheckBox.Checked)
            {
                using (StreamWriter streamWriter = new StreamWriter(File.Open(saveFileDialog.FileName, FileMode.OpenOrCreate)))
                {
                    streamWriter.Write(contentTextBox.Text);
                }
            }
            else
            {
                UnicodeEncoding encoding = new UnicodeEncoding();
                using (BinaryWriter binaryWriter = new BinaryWriter(File.Open(saveFileDialog.FileName, FileMode.OpenOrCreate)))
                {
                    binaryWriter.Write(encoding.GetBytes(contentTextBox.Text));
                }
            }
.
.
.

Using streams to write data is very similar whether we're writing text or binary, although you do have to use different objects: StreamWriter for text, and BinaryWriter for binary. As with reading, we're again taking advantage of the using syntax. We use the Write method of either object to save the contents, although for binary we are encoding the data again in the UTF8 format. You might also note that we're using the OpenOrCreate FileMode mode, which opens the file if it already exists, or creates it if it doesn't.

Before you move on to the next lesson, do your homework! Right-click in the window where this lesson text appears and select Back. Then select Quiz for this lesson in the syllabus and answer the quiz questions. When you finish the quiz questions, click Hand it in at the bottom of that window. Then do the same with the Project(s) for the lesson. Your instructor will grade your quiz(zes) and project(s) and provide guidance if needed.