How To Persist Windows Form State

Published:
Updated:
Sometimes when developing windows forms application, it becomes an important part of application design to persist the state of windows form after it is closed.

One such example that comes to my mind is an application called “Sticky Notes”.

Here the user is expected to open a notepad which she can re-size as and when required, can write few notes on it and then close this form. The notes are to be saved in a persistent storage like Database or XML.

Next time when the user opens that sticky note, it should open in the same size and display the notes that were written by the user last time.

As far as persistence of the notes to Database and/or XML file is concerned, I know there are many ways to do it. This article however deals with the second part of the problem, i.e. persistence of the form state in the application.

Some people might argue that if we can save note’s data in DB/XML we can also save form’s State. But, i personally feel that since form’s state is the data related to user interface, so it should be persisted somewhere in the application.

So let’s begin this tutorial by understanding the problem statement first:-

The Problem Statement

During the flow of an application, a windows form is opened. The User the re-sizes the form, make some other activity with the form and closes the form. Next time when the user opens this same form again, the size of the form should be same as when the form was closed.

Solution

Looking at the problem statement, as an application developer we can make out the following requirements:-

1.


We need a mechanism to save the size of the form before the form closes

2.


We need a mechanism to reload the saved state and apply this state to the form  before the form loads on screen.

Serialization Comes To Rescue

If I have to put it in simple language, Serialization is the mechanism to convert an object graph to an array of bytes. De-Serialization is vice-verse.

Right from the time .net came to existence; I personally feel serialization has been among the top features offered by the framework. It is truly very simple to serialize an object graph to array of bytes.

The following few lines is just what we need to convert an object graph to memory stream:-

Stream memStream=new MemoryStream();
                      IFormatter formatter=new BinaryFormatter();
                      formatter.serialize(memStream, object);

Open in new window


Here is how to de-serialize it:-
 
Object obj = formatter.deserialize(memStream);

Open in new window


So, let’s focus back on our problem. Let’s build the infrastructure to hold the persistence logic.

A form state wrapper

Let’s first make a type which will hold the form’s state that can be persisted later on. Let’s name the class FormState and start with holding the form’s window state and form’s size in the FormState Type.  Next since we need to save this object through serialization, we need to mark it  as Serializable.

So the class looks like this:-

    [Serializable]
                          internal class FormState
                          {
                              private readonly FormWindowState _windowState;
                              private readonly Point _formSize;
                      
                              private FormState()
                              {
                              }
                      
                              public FormState(FormWindowState state, int width, int height)
                              {
                                  _windowState = state;
                                  _formSize = new Point(width, height);
                              }
                      
                              public FormWindowState WindowState
                              {
                                  get { return _windowState; }
                              }
                      
                              public Point Size
                              {
                                  get { return _formSize; }
                              }
                           }

Open in new window


Also we have created a private parameter less constructor in FormState as is required at the time of serialization.

A FormState collection

OK, so now we have a type to store form’s state information. Next let’s make a type to hold this state object of various forms. We need a dictionary where the key would be the form’s name and value would be an instance of FormState.

Let’s name the type as FormStateCollection as it will hold a collection of FormState Type.

Again this will also be marked serializable as this is the penultimate type that we are going to serialize and de-serialize. You can also say that this type is more of a state bag that will hold state information for all the forms.

Here is how the type looks like:-

    [Serializable]
                          class FormStateCollection
                          {
                              private IDictionary<string, FormState> _formPersistanceHandlers;
                              public static FormStateCollection Instance { get; private set; }
                      
                      
                              static FormStateCollection()
                              {
                                  Instance =
                                      FileHelper.ReadFromFile<FormStateCollection>
                                          (string.Format(Resources.FilePath,  Directory.GetCurrentDirectory())) ??
                                      new FormStateCollection();
                              }
                              
                              #region Methods
                      
                              /// <summary>
                              /// Adds Persistance Handler To The Dictionary(Thread Safe Way)
                              /// </summary>
                              /// <param name="formName"></param>
                              /// <param name="handler"></param>
                              [MethodImpl(MethodImplOptions.Synchronized)]
                              public void Persist(string formName, FormState handler)
                              {
                                  if(!IsInitialized()) _formPersistanceHandlers = new Dictionary<string, FormState>();
                      
                                  if(ContainsKey(formName))
                                  {
                                      _formPersistanceHandlers[formName] = handler;
                                  }
                                  else
                                  {
                                      _formPersistanceHandlers.Add(formName, handler);
                                  }
                                  
                              }
                      
                              /// <summary>
                              /// Gets The Persistance Handler For Particular Form
                              /// </summary>
                              /// <param name="formName"></param>
                              /// <returns></returns>
                              public FormState ReadState(string formName)
                              {
                                  if (!IsInitialized() || !ContainsKey(formName)) return null;
                      
                                  return _formPersistanceHandlers[formName];
                              }
                      
                              /// <summary>
                              /// Checks Whether The Dictionary Has Been Initialized or Not
                              /// </summary>
                              /// <returns></returns>
                              private bool IsInitialized()
                              {
                                  return _formPersistanceHandlers != null;
                              }
                      
                              /// <summary>
                              /// If Dictionary Contains This Key
                              /// </summary>
                              /// <param name="key"></param>
                              /// <returns></returns>
                              private bool ContainsKey(string key)
                              {
                                  return _formPersistanceHandlers.ContainsKey(key);
                              }
                      
                              #endregion
                          }

Open in new window


The collection implements a singleton so that for entire application we can have a single collection that can be read from and written to by any form.

The two most important behaviors of this collection are to save the form state and read the persisted state. The two methods namely Persist and ReadState are implementing the functionality.

OK, so we had enough of infrastructure, let’s put this to work and create our concrete types that forms are going to work with in the application.

The PersistantForm

If you could remember the initial requirements list that we made, we need to read from saved state before form load and save the state at the time when the form closes.

So we need to bind to the form’s load and close events. This means we need a form that can act as the base form for all the forms that need their state to be persisted.

Let’s make an base form namely PersistantForm and bind to its load and closing events.

In the form load event handler, we will read from the saved state.
In the form closing event handler, we will save the form’s current state.


Let’s have a look at this base form:-
public class PersistantForm : Form
                          {
                              public PersistantForm()
                              {
                                  this.Load += PersistantFormBaseLoad;
                                  this.FormClosing += PersistantFormBaseFormClosing;
                              }
                      
                              /// <summary>
                              /// Update Our Persistable Forms Type & Serialize It
                              /// </summary>
                              /// <param name="sender"></param>
                              /// <param name="e"></param>
                              void PersistantFormBaseFormClosing(object sender, FormClosingEventArgs e)
                              {
                                  FormStateCollection.Instance.Persist(this.GetType().FullName, ReadCurrentState());
                              }
                      
                              /// <summary>
                              /// Apply Last Saved State To This Form
                              /// </summary>
                              /// <param name="state"></param>
                              internal virtual void ApplyState(FormState state)
                              {
                                  this.WindowState = state.WindowState;
                                  this.Size = new Size(state.Size);
                      
                                  /********************************
                                   * You Can Also Read any other information
                                   * Persisted for this Form by calling
                                   * FormState ReadCustomState method like this:-
                                   * state.ReadCustomState<Rectangle>("key");
                                   ********************************/
                              }
                      
                              /// <summary>
                              /// Reads The Previous Saved State of this form
                              /// </summary>
                              /// <returns></returns>
                              private FormState ReadLastState()
                              {
                                  return FormStateCollection.Instance.ReadState(this.GetType().FullName);
                              }
                      
                              
                      
                        /// <summary>
                              /// Gets The Current State Of This Form
                              /// </summary>
                              /// <returns></returns>
                              internal virtual FormState ReadCurrentState()
                              {
                                  var currState = new FormState(this.WindowState, this.Size.Width, this.Size.Height);
                                  return currState;
                      
                                  /********************************
                                   * You Can Also Persist any other information
                                   * by calling FormState AddCustomState method:-
                                   * currState.AddCustomState("key","value");
                                   ********************************/
                              }
                          } //End of class

Open in new window

That’s it, now any form that need to persist its state, can simply derive from this form base class and its state will automatically be saved and retrieved as a part of form life cycle.

What about other form states

All looks good till now, but simply saving the form’s size and window state doesn’t help. We might need to store other information as well in our form type, so I have kept a dictionary in the FormState class to hold other state information.

Also there are few methods to work around with this state dictionary.

So that’s it folks, we have made an infrastructure which can be used to persist the Form’s state and then read from that state.

Go ahead and download the code attached.

Happy Programming.....
FormPersist.zip
4
11,241 Views

Comments (2)

Commented:
Great article! I especially appreciate the comment about separating the application data from the UI specific data. It is easy to forget the difference when working on an app that has to be done "Right Now" (not that things are ever done that way..).
Easwaran ParamasivamSenior Software Engineer

Commented:
Excellent Article!!

Have a question about something in this article? You can receive help directly from the article author. Sign up for a free trial to get started.