Click here to Skip to main content
15,867,686 members
Articles / Programming Languages / C#
Article

Save and restore the state of a Windows Form in .NET 2.0

Rate me:
Please Sign up or sign in to vote.
3.61/5 (9 votes)
19 May 20062 min read 69.4K   29   17
A class that will save and restore window state, size, and position for a Form in .NET 2.0.

Introduction

The class saves the state (location, size and windows state) of a form to registry (under HKEY_CURRENT_USER) when the form is closed and restores the form state when it is loaded. (To work with .NET 1.x one need to use some other events and a couple of methods are new to .NET 2.0).

It will also work with multi screen desktops. The key here is to use Form.DesktopBounds and not Form.Location and Form.Size. Also i case of screens not always being there I use Screen.GetBounds() to check and if needed move form to an existing screen.

To use just include the following line in the constructor of your Form:

FormState formState = new FormState(this, "SampleApp");

Also got some methods for storing and getting other settings regarding the form in registry (SaveValue(), GetValue(), GetIntValue()). Use these in the FormClosed and Load event handlers store and restore whatever... (need to keep the FormState object as a variable of your form class in this case).

The class:

using System;
using System.Drawing;
using Microsoft.Win32;
using System.Windows.Forms;

/// <summary>
/// Create an instance of this class in the constructor of a form. 
/// Will save and restore window state, size, and position.
/// Uses DesktopBounds (instead of just Form.Location/Size) 
/// to place window correctly on a multi screen desktop.
/// </summary>
class FormState
{
  private Form _parent;
  private string _registry_key;

  /// <summary>
  /// Initializes an instance of the FormState class.
  /// </summary>
  /// <param name="parent">
  /// The form to store settings for.
  /// </param>
  /// <param name="sub_key">
  /// Registry path from HKEY_CURRENT_USER to place for storing settings.
  /// Will create a subkey named "FormState".
  /// </param>
  public FormState(Form parent, string subkey)
  { 
    this._parent = parent;
    this._registry_key = subkey + "\\FormState";
    this._parent.Load += new EventHandler(On_Load);
    this._parent.FormClosed += new FormClosedEventHandler(On_FormClosed);
  }

  public void SaveValue(string name, object value)
  {
    this.RegKey.SetValue(name, value);
  }

  public object GetValue(string name, object default_value)
  {
    return this.RegKey.GetValue(name, default_value);
  }

  /// <summary>
  /// If for some reason the value stored in reg cannot be parsed to int 
  /// the default_value is returned.
  /// </summary>
  public int GetIntValue(string name, int default_value)
  {
    int val = default_value;
    if (!int.TryParse(this.RegKey.GetValue(name, default_value).ToString(), out val))
      val = default_value;
    return val;
  }

  private RegistryKey RegKey
  { 
    get 
    { 
      return Registry.CurrentUser.CreateSubKey(
        this._registry_key + "\\" + this._parent.Name); 
    } 
  }

  private void On_Load(object sender, EventArgs e)
  {
    int X, Y, width, height, window_state;

    // place to get settings from
    RegistryKey key = this.RegKey;

    if (!int.TryParse(key.GetValue("DesktopBounds.Width", 
      this._parent.DesktopBounds.Width).ToString(), 
      out width))
      width = this._parent.DesktopBounds.Width;
    if (!int.TryParse(key.GetValue("DesktopBounds.Height", 
      this._parent.DesktopBounds.Height).ToString(), 
      out height))
      height = this._parent.DesktopBounds.Height;
    if (!int.TryParse(key.GetValue("DesktopBounds.X", 
      this._parent.DesktopBounds.X).ToString(), 
      out X))
      X = this._parent.DesktopBounds.X;
    if (!int.TryParse(key.GetValue("DesktopBounds.Y", 
      this._parent.DesktopBounds.Y).ToString(), 
      out Y))
      Y = this._parent.DesktopBounds.Y;

    // In case of multi screen desktops, check if we got the
    // screen the form was when closed.
    // If not there we put it in upper left corner of nearest 
    // screen.
    // We don't bother checking size (as long as the user see
    // the form ...).
    Rectangle screen_bounds = Screen.GetBounds(new Point(X, Y));
    if (X > screen_bounds.X + screen_bounds.Width)
    {
      X = screen_bounds.X;
      Y = screen_bounds.Y;
    }

    this._parent.DesktopBounds = new Rectangle(X, Y, width, height);
    
    if (!int.TryParse(key.GetValue("WindowState", 
      (int)this._parent.WindowState).ToString(), 
      out window_state))
      window_state = (int)this._parent.WindowState;
    
    this._parent.WindowState = (FormWindowState)window_state;
  }

  private void On_FormClosed(object sender, FormClosedEventArgs e)
  {
    // There may be cases where the event is raised twice.
    // To avoid handling it twice we remove the handler.
    this._parent.FormClosed -= new FormClosedEventHandler(On_FormClosed);
      // TODO: find out why it is raised twice ...

    // place to store settings
    RegistryKey key = this.RegKey;

    // save window state
    key.SetValue("WindowState", (int)this._parent.WindowState);
    
    // save pos & size in normal window state
    if (this._parent.WindowState != FormWindowState.Normal)
      this._parent.WindowState = FormWindowState.Normal;
    key.SetValue("DesktopBounds.Y", this._parent.DesktopBounds.Y);
    key.SetValue("DesktopBounds.X", this._parent.DesktopBounds.X);
    key.SetValue("DesktopBounds.Width", this._parent.DesktopBounds.Width);
    key.SetValue("DesktopBounds.Height", this._parent.DesktopBounds.Height);
  }
}

A note on the FormClosed event. When the main form (used in Application.Run()) is closed, the application quits and all forms created after the main form was created are also closed. One would think that the FormClosing and FormClosed event would be raised for all forms. This does not seem to be the case.

I solve this by using the following in the main form:

private void On_FormClosing(object sender, FormClosingEventArgs e)
{
  this.FormClosing -= new FormClosingEventHandler(On_FormClosing);
  Application.Exit(e);
}

private void On_FormClosed(object sender, FormClosedEventArgs e)
{
  this.FormClosed -= new FormClosedEventHandler(On_FormClosed);
}
in the FormClosing event for the main form of the application. This raises the FormClosing and FormClosed event for all open forms.

Passing the FormClosingEventArgs param to Application.Exit() gives the other forms a chance to cancel quitting the application.

Removing the FormClosing and FormClosed delegates prevents the methods from being called a second time (as Application.Exit() will raise the events for the main form as well as the others).


This class was created after having used various laboursome methods over the years and finally getting tired of it.

Various articles and blogs on the internet including an article ("Saving and Restoring the Location, Size and Windows State of a .NET Form") by Joel Matthias here on the Code Project was the inspiration for this class.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Norway Norway
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionReloading panel contents Pin
deb198215-Apr-07 17:47
deb198215-Apr-07 17:47 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.