Persisting Your Visual Basic .NET Objects with .NET Serialization
If you're developing a standalone .NET application, more than likely you'll need to provide your users with the ability to save and later load data. There are many ways to approach such a task. The wrong way to do it is to open a binary file and write the individual data strings and numbers out to the stream, passing each data item in a particular order, and rebuilding everything on the way back in. The correct — and much easier — way is to use the .NET Framework's set of classes that make saving your data far easier. Such a process is called serialization, and the data is said to be serialized. The serialization classes make saving your data so easy it's almost trivial. I'm not kidding. And that's the way it should be. If you're writing a program that models molecules, for example, your job should be focusing on the molecular modeling, not figuring out a way to save data to a file.
In this article, I'll show you how to serialize the data, diving in with a set of complex data just to show you how easy it really is. Then I'll show you some ways to extend the concept to support different types of data, as well as versioning.
Saving the Data
Before we get started, we need some data to save. Rather than trying to build an entire molecular modeling program, I’ll use an example from a recent test program I was building that renders text and fonts, letting the user customize everything. Here’s the user data that needs to be saved:
- Current font. This is an instance of the class System.Drawing.Font.
- Rendering hint. This is an enumeration of type System.Drawing.Text.TextRenderingHint and deals with anti-aliasing and smoothing.
- Font color and background color. Both are instances of the class System.Drawing.Color.
- Contrast. This is an integer used in the text rendering.
- Text to be rendered. This is a string.
Sometimes programmers lose sight of the concept, but it’s true for most programs: Software is about modifying data. When you’re using a word processing program, you’re modifying a set of data that consists of the text you’re typing along with font and layout information. Regardless of the software, if you think of the user data as a single entity or collection, serializing becomes easier. In our case, that means lumping all the previous data into a single class. Here’s what our class will look like:
<Serializable()> Class TextSettings Public CurrentFont As Font Public CurrentHint As TextRenderingHint Public FontColor As Color = Color.Black Public BackgroundColor As Color = Color.LightGray Public Contrast As Int32 = 0 Public Text As String = "" End Class
For this example, I made the members public. In a more realistic case, you would use properties and various protected members and such, along with member subroutines and functions, to manipulate the data.
For the class to be serializable, you need to add a .NET attribute. That’s the <Serializable()> at the beginning of the class declaration. An attribute is simply an additional class associated with the object. In this case, the additional class is called Serializable, and we call the class the Serializable attribute. Each instance of our class TextSettings has an instance of Serializable associated with it. To use the serialization features of .NET, you need to make a class serializable by adding the Serializable attribute.
The next step is to add the code to save the data. You would use this code either as a button handler or as a menu handler. My program had a toolbar and no menu. Here’s the code I used, which handles the click of the Save button:
Private Sub SaveToolStripButton_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles SaveToolStripButton.Click If SaveFileDialog1.ShowDialog = Windows.Forms.DialogResult.OK Then Dim formatter As New _ Runtime.Serialization.Formatters.Binary.BinaryFormatter Dim stream As New FileStream(SaveFileDialog1.FileName, _ FileMode.Create, FileAccess.Write, FileShare.None) formatter.Serialize(stream, TextValues) stream.Close() End If End Sub
To use this code, you’ll need an Imports System.IO line at the top of the code file. Then, believe it or not, you have everything you need to save your data. This code opens a SaveFileDialog dialog box so the user can choose the filename and directory for the data file. If the user clicks OK, the code inside the If block runs.
The If block creates a new instance of the class called BinaryFormatter. Then the code creates a new file stream using the filename that the user chose. Next, the code calls the Serialize method of BinaryFormatter, passing the stream and the instance of the data. (TextValues is an instance of our data class, TextSettings). Finally, the code closes the stream.
That’s it! That’s all it takes! It couldn’t be easier. Nowhere did I have to write out the individual members manually; I didn’t even refer to the individual members in the code.
Now I’ll show you the code for reading the data.