conceptual inertia

Just another WordPress weblog

custom serialization

without comments

For a derivative class to be serializable, the base class must be marked as serializable as well. For example, if I do the following:


[Serializable]
public partial class Form1 : Form
 

And attempt to serialize the object, I will get an exception.

Adding a serializable attribute to the base Form is not possible and many people at this point move on looking for another solution. However, it is possible to make Form1 serializable through custom serialization. To do this, first add ISerializable to the inheritance chain:


[Serializable]
public partial class Form1 : Form, ISerializable
 

And then implement it:


void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
{
    info.AddValue("Location", this.Location);
    info.AddValue("ClientSize", this.ClientSize);
}
 

An exception is no longer produced when running the code now. After execution, you should find a ”Form1.bin” file in the Debug directory. Despite this, a binary file is not very user-friendly. Let’s modify the code so we can actually open the file and read it. Rather than using a BinaryFormatter, why not a SoapFormatter? To use soap formatting you’ll need to add a reference to the assembly:


using System.Runtime.Serialization.Formatters.Soap;
//...

FileStream fileStream = File.Create("Form1.xml");
IFormatter iFormatter = new SoapFormatter();
iFormatter.Serialize(fileStream, form);
 

In the above code, I’ve changed the file name from Form1.bin to Form1.xml. However, the file extension doesn’t impact the content. Here’s a sample of the output:

So, serialization works but what about deserialization? To accomplish that, you need to implement a deserialization constructor:


private Form1(SerializationInfo info, StreamingContext context)
{
    DefaultInitialization();
    this.Location = (Point)info.GetValue("Location", this.Location.GetType());
    this.ClientSize = (Size)info.GetValue("ClientSize", this.ClientSize.GetType());
}
 

The DefaultInitialization function calls InitializeComponent and then assigns some default values. Calling InitializeComponent is important if you want the Form and any of its controls to be initialized appropriately.


private void DefaultInitialization()
{
    InitializeComponent();
    this.StartPosition = FormStartPosition.Manual;
    this.Location = new Point(0, 0);
    this.ClientSize = new Size(640, 480);
}
 

The DefaultInitialization function is also called from the default constructor:


public Form1()
{
    DefaultInitialization();
}
 

Deserialization can’t occur after Application.Run returns because the Form has already been destroyed. Here, we plug-in to the FormClosing event to handle deserialization:


private void OnFormClosing(object sender, FormClosingEventArgs e)
{
    FileStream fileStream = new FileStream("Form1.xml",
                                           FileMode.Create,
                                           FileAccess.Write);

    IFormatter iFormatter = new SoapFormatter();
    iFormatter.Serialize(fileStream, this);

    fileStream.Close();
}
 

Finally, we can bring it all together in Main:


[STAThread]
static void Main()
{
    Form1 form = null;

    if (File.Exists("Form1.xml"))
    {
        FileStream fileStream = File.Open("Form1.xml", FileMode.Open);
        IFormatter iFormatter = new SoapFormatter();
        form = iFormatter.Deserialize(fileStream) as Form1;

        fileStream.Close();
    }

    if (form == null)
        form = new Form1();

    Application.Run(form);
}
 

With this simple example, you can position and resize the Form; restart the application and it will remember your previous settings. Now I’m not saying this is a way to save Form settings. There are numerous methods including:

  • app.config
  • .resources
  • .ini
  • manual parsing of various file formats

The real demonstration here is how to make an object serializable when the base class isn’t.

Written by admin

June 2nd, 2006 at 12:52 am

Posted in code

Leave a Reply

You must be logged in to post a comment.