Click here to Skip to main content
Click here to Skip to main content

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

, 19 May 2006
Rate this:
Please Sign up or sign in to vote.
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

Share

About the Author

No Biography provided

Comments and Discussions

 
GeneralRetaining the order in which the windows are opened Pinmemberjuhi0923-Sep-08 19:59 
GeneralAn example using My.Settings PinmemberHolm7630-Jul-07 22:38 
Hi
 
I found your example searching for a way to save WindowState. I like your example very much. In fact so much that I based my own code from your example and extented it.
 
All you have to do to use my code is to create the 5 My.Settings variables and create an instance of FormState in your main form. Everything then happens automatically heron after. This works for the main form only. If you create more instances for FormState in other subforms those forms will overwrite the location and state data for the main form. So please bear that in mind if you use it.
 
Ive updated the routine that checks if the window is at a valid location. If it isnt it is moved onto the closest screen. Instead of using GetBounds Ive used GetWorkingArea. GetWorkingArea returns a regtangle where the taskbar and other docked apps like the sidebar on vista is subtracted. I find that if a program should be moved it should be moved completly into view without cover from said taskbar or sidebar or another docked tool.
 
I also added a few other events so it can control and restore correctly after shutdown from minimized state.
 
Here is the VB.NET code:
 

Public Class FormState
 
Private parent As Form
 
Public Sub New(ByVal parent As Form)
Me.parent = parent
AddHandler parent.Load, AddressOf Load
AddHandler parent.ResizeEnd, AddressOf ResizeEnd
AddHandler parent.FormClosed, AddressOf FormClosed
End Sub
 
Private Sub Load(ByVal sender As Object, ByVal e As EventArgs)
 
Dim X As Integer = My.Settings.WindowLocationX
Dim Y As Integer = My.Settings.WindowLocationY
Dim width As Integer = My.Settings.WindowWidth
Dim height As Integer = My.Settings.WindowHeight
Dim windowState As Integer = My.Settings.WindowState
 
Dim screenBounds As Rectangle = Screen.GetWorkingArea(New Point(X, Y))
 
If X > screenBounds.X + screenBounds.Width Then
X = screenBounds.X + screenBounds.Width - width
ElseIf X + width < screenBounds.X Then
X = screenBounds.X
End If
 
If Y > screenBounds.Y + screenBounds.Height Then
Y = screenBounds.Y + screenBounds.Height - height
ElseIf Y < screenBounds.Y Then
Y = screenBounds.Y
End If
 
Me.parent.DesktopBounds = New Rectangle(X, Y, width, height)
Me.parent.WindowState = windowState
 
AddHandler parent.ClientSizeChanged, AddressOf ClientSizeChanged
End Sub
 
Private Sub ResizeEnd(ByVal sender As Object, ByVal e As EventArgs)
My.Settings.WindowLocationX = Me.parent.DesktopBounds.X
My.Settings.WindowLocationY = Me.parent.DesktopBounds.Y
My.Settings.WindowWidth = Me.parent.DesktopBounds.Width
My.Settings.WindowHeight = Me.parent.DesktopBounds.Height
End Sub
 
Private Sub ClientSizeChanged(ByVal sender As Object, ByVal e As EventArgs)
If Not Me.parent.WindowState = FormWindowState.Minimized Then
My.Settings.WindowState = Me.parent.WindowState
End If
End Sub
 
Private Sub FormClosed(ByVal sender As Object, ByVal e As FormClosedEventArgs)
If Not Me.parent.WindowState = FormWindowState.Minimized Then
My.Settings.WindowState = Me.parent.WindowState
End If
If Me.parent.WindowState = FormWindowState.Normal Then
My.Settings.WindowLocationX = Me.parent.DesktopBounds.X
My.Settings.WindowLocationY = Me.parent.DesktopBounds.Y
My.Settings.WindowWidth = Me.parent.DesktopBounds.Width
My.Settings.WindowHeight = Me.parent.DesktopBounds.Height
End If
End Sub
 
End Class

 
Again all you have to do to use this is create the five My.Settings variables and create an instance of the class in your main form in your decleration area.
 
Private formState As New FormState(Me)

QuestionReloading panel contents Pinmemberdeb198215-Apr-07 17:47 
GeneralRestore minimized form Pinmemberjodvova17-Aug-06 0:27 
GeneralUnsubscribing Closing/Closed event handlers PinmemberDelta-Z5-Aug-06 4:28 
Questionwhy using the registry? Pinmembermayafit20-May-06 5:35 
AnswerRe: why using the registry? PinmemberXaroth20-May-06 11:54 
GeneralRe: why using the registry? PinmemberStein-Tore Erdal20-May-06 22:56 
GeneralRe: why using the registry? PinmemberSyed Moshiur Murshed21-May-06 1:40 
GeneralRe: why using the registry? PinmemberStein-Tore Erdal21-May-06 2:34 
GeneralRe: why using the registry? Pinmembermayafit6-Sep-06 20:02 
GeneralRe: why using the registry? PinmemberAn Englishman in Norway23-Nov-08 10:15 
GeneralFor .NET 2.0, use ConfigurationManager PinmemberITGFanatic14-Dec-06 8:34 
GeneralMultiple Monitors PinprotectorMarc Clifton20-May-06 1:50 
GeneralRe: Multiple Monitors PinmemberStein-Tore Erdal20-May-06 2:43 
GeneralRe: Multiple Monitors PinprotectorMarc Clifton20-May-06 2:44 
GeneralRe: Multiple Monitors [modified] PinmemberSimonO9-Jul-07 14:46 

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

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

| Advertise | Privacy | Mobile
Web03 | 2.8.140814.1 | Last Updated 20 May 2006
Article Copyright 2006 by Stein-Tore Erdal
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid