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

Saving and Restoring the Location, Size and Windows State of a .NET Form

Rate me:
Please Sign up or sign in to vote.
4.60/5 (20 votes)
5 Mar 20022 min read 184K   3K   84   32
A simple class to automatically save and restore a form's position size and window state.

Introduction

Every time I create a new desktop application I find myself having to add some code that will restore the main application window to the position, size and window state at the time of closing. This article presents a simple C# class that may be added to a form to automatically do this. When I designed this class I wanted to be able to add it to a form using the least amount of code and also be able to add it to a form from the toolbox.

The class is named PersitWindowState and using it in a form is very simple. The simplest usage of the class is shown below:

C#
public class AppForm : System.Windows.Forms.Form
{
    private PersistWindowState m_windowState;

    public AppForm()
    {
        m_windowState = new PersistWindowState();
        m_windowState.Parent = this;

        // set registry path in HKEY_CURRENT_USER
        m_windowState.RegistryPath = @"Software\YourCompany\YourApp"; 
    }

    [STAThread]
    static void Main() 
    {
        Application.Run(new AppForm());
    }
}

This is all the code that is required to automatically save and load the window state correctly. I have also added an additional feature to PersistWindowState that facilitates loading and saving any additional form state information. The form can subscribe to two events PersistWindowState.LoadStateEvent and PersistWindowState.SaveWindowState. These events are fired with a RegistryKey instance allowing the form to save and load additional values. The code below illustrates the use of this feature:

C#
public class AppForm : System.Windows.Forms.Form
{
    private PersistWindowState m_windowState;

    public AppForm()
    {
        this.Text = "RestoreFormState";

        m_windowState = new PersistWindowState();
        m_windowState.Parent = this;

        // set registry path in HKEY_CURRENT_USER
        m_windowState.RegistryPath = @"Software\YourCompany\YourApp"; 
        
        // subscribe to the load and save events
        m_windowState.LoadStateEvent += 
            new PersistWindowState.WindowStateDelegate(LoadState);
        m_windowState.SaveStateEvent += 
            new PersistWindowState.WindowStateDelegate(SaveState);
    }

    private int m_data = 34;

    private void LoadState(object sender, RegistryKey key)
    {
        // get additional state information from registry
        m_data = (int)key.GetValue("m_data", m_data);
    }

    private void SaveState(object sender, RegistryKey key)
    {
        // save additional state information to registry
        key.SetValue("m_data", m_data);
    }

    [STAThread]
    static void Main() 
    {
        Application.Run(new AppForm());
    }
}

Let's now take a look at PersistWindowState itself. The key to this class is the ability of an instance of any class to subscribe to the events of any other class. PersistWindowState subscribes to 4 events of it's parent's class namely Form.Closing, Control.Resize, Control.Move and Form.Load. These events are subscribed to when the Parent property is set (a good example of the usefulness of property functions).

The current state of the form is recorded in the two events Control.Resize and Control.Move. Control.Resize allows us to record the current form width and height. Note that we only do this if the current window state is normal. If we don't then we end up saving the size of the maximized or minimized window which is not what we want. The Control.Move event is used to record the form's position (agin only if the window state is normal) and the current window state.

The saving and loading of registry data is handled in response to Form.Closing and Form.Load respectively. When I first developed this class in .NET Beta 1 I found that restoring the form state in response to Form.Load caused the form to visibly move. This does not appear to happen in the released version of .NET.

Here is the complete code for PersistWindowState:

C#
public class PersistWindowState : System.ComponentModel.Component
{
    // event info that allows form to persist extra window state data
    public delegate void WindowStateDelegate(object sender, RegistryKey key);
    public event WindowStateDelegate LoadStateEvent;
    public event WindowStateDelegate SaveStateEvent;

    private Form m_parent;
    private string m_regPath;
    private int m_normalLeft;
    private int m_normalTop;
    private int m_normalWidth;
    private int m_normalHeight;
    private FormWindowState m_windowState;
    private bool m_allowSaveMinimized = false;

    public PersistWindowState()
    {
    }

    public Form Parent
    {
        set
        {
            m_parent = value;

            // subscribe to parent form's events
            m_parent.Closing += new System.ComponentModel.CancelEventHandler(OnClosing);
            m_parent.Resize += new System.EventHandler(OnResize);
            m_parent.Move += new System.EventHandler(OnMove);
            m_parent.Load += new System.EventHandler(OnLoad);

            // get initial width and height in case form is never resized
            m_normalWidth = m_parent.Width;
            m_normalHeight = m_parent.Height;
        }
        get
        {
            return m_parent;
        }
    }

    // registry key should be set in parent form's constructor
    public string RegistryPath
    {
        set
        {
            m_regPath = value;        
        }
        get
        {
            return m_regPath;
        }
    }

    public bool AllowSaveMinimized
    {
        set
        {
            m_allowSaveMinimized = value;
        }
    }

    private void OnResize(object sender, System.EventArgs e)
    {
        // save width and height
        if(m_parent.WindowState == FormWindowState.Normal)
        {
            m_normalWidth = m_parent.Width;
            m_normalHeight = m_parent.Height;
        }
    }

    private void OnMove(object sender, System.EventArgs e)
    {
        // save position
        if(m_parent.WindowState == FormWindowState.Normal)
        {
            m_normalLeft = m_parent.Left;
            m_normalTop = m_parent.Top;
        }
        // save state
        m_windowState = m_parent.WindowState;
    }

    private void OnClosing(object sender, System.ComponentModel.CancelEventArgs e)
    {
        // save position, size and state
        RegistryKey key = Registry.CurrentUser.CreateSubKey(m_regPath);
        key.SetValue("Left", m_normalLeft);
        key.SetValue("Top", m_normalTop);
        key.SetValue("Width", m_normalWidth);
        key.SetValue("Height", m_normalHeight);

        // check if we are allowed to save the state as minimized (not normally)
        if(!m_allowSaveMinimized)
        {
            if(m_windowState == FormWindowState.Minimized)
                m_windowState = FormWindowState.Normal;
        }

        key.SetValue("WindowState", (int)m_windowState);
        
        // fire SaveState event
        if(SaveStateEvent != null)
            SaveStateEvent(this, key);
    }

    private void OnLoad(object sender, System.EventArgs e)
    {
        // attempt to read state from registry
        RegistryKey key = Registry.CurrentUser.OpenSubKey(m_regPath);
        if(key != null)
        {
            int left = (int)key.GetValue("Left", m_parent.Left);
            int top = (int)key.GetValue("Top", m_parent.Top);
            int width = (int)key.GetValue("Width", m_parent.Width);
            int height = (int)key.GetValue("Height", m_parent.Height);
            FormWindowState windowState = (FormWindowState)key.GetValue("WindowState", 
                            (int)m_parent.WindowState);

            m_parent.Location = new Point(left, top);
            m_parent.Size = new Size(width, height);
            m_parent.WindowState = windowState;

            // fire LoadState event
            if(LoadStateEvent != null)
                LoadStateEvent(this, key);
        }
    }
}

You may have noticed a property AllowSaveMinimized, if this is set to true then if the form is minimized when it is closed it will be minimized when it is reloaded. This behaviour is probably not what you want to happen so it is set to falseby default.

Well I hope a few people find this class useful and that some have learned a little from it.

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



Comments and Discussions

 
QuestionPositions of form above another form Pin
Member 790431112-Jul-16 19:07
Member 790431112-Jul-16 19:07 
QuestionGreat! Thx! You helped me a lot! Pin
Andras Szekely (HUNGARY)8-Apr-13 13:29
Andras Szekely (HUNGARY)8-Apr-13 13:29 
GeneralGood inspiration Pin
Zijian7-Mar-07 19:54
Zijian7-Mar-07 19:54 
GeneralOne line is enough Pin
EfimPechat23-Feb-07 10:32
EfimPechat23-Feb-07 10:32 
GeneralUse the form's normal state size Pin
Tam Myaing11-Jan-07 4:33
Tam Myaing11-Jan-07 4:33 
NewsMultiple Screens/Monitors Pin
Christopher Scholten13-Dec-05 21:04
professionalChristopher Scholten13-Dec-05 21:04 
One thing that most people forget when saving Window Positions is that some people use multiple screens. If this isn't taken into consideration when saving/retrieving window coordinates, windows can be 'lost' if the positioning of a secondary monitor is changed in between uses of an application, causing a user great frustration. Microsoft Photo Editor (included with Office 2000) had this problem, and I had to go into the registry to fix the screen position if I changed my screen configurations between uses.

Window positions are saved with respect to the primary monitor's top left corner (this being x,y coordinate 0,0). This means that if a secondary monitor (Set up in Windows Display Properties) was positioned to the left and above the primary monitor, then it would have negative form.Top and form.Left properties. Now all this is fine, until the configuration of the monitors change. Consider the following two scenarios:

A - Laptop User> I use a laptop, and when it's docked, I use a dual monitor configuration. If I exit an application which saves a form's position, where that form is on the non-primary monitor, then undock my laptop (leaving only a primary monitor - the laptop monitor), then when I re-run the application, if it doesn't do a check to see whether the form is able to be displayed, then the form is lost.

B - Repositioning of 2nd Form> I have two monitors, and the second is positioned to the left of the primary monitor. I exit an application that saves form position, leaving the form on the secondary monitor. Once the application is closed, I reposition the secondary monitor to be to the right of the first. When re-starting the application, the window will be lost unless a check is performed to determine whether the form is visible.

The following code, which I have used in a static class, performs a check on a form to ensure it is visible, and if not, partially repositions the form to ensure the user can adjust it.

<br />
/// <summary><br />
/// Ensures that the form is visible on the current screen - <br />
/// to be called after the form has been repositioned based <br />
/// on saved settings<br />
/// </summary><br />
/// <param name="form">Form to be shown on screen</param><br />

<br />
public static void EnsureFormVisible(<br />
	System.Windows.Forms.Form	form) <br />
{<br />
	#region Ensure that the window is visible<br />
	Screen currentScreen = Screen.FromHandle(form.Handle);<br />
	// Ensure top visible<br />
	if((form.Top < currentScreen.Bounds.Top) ||<br />
		((form.Top + form.Height) > (currentScreen.Bounds.Top + currentScreen.Bounds.Height)))<br />
	{<br />
		form.Top = currentScreen.Bounds.Top;<br />
	}<br />
	// Ensure at least 60 px of Title Bar visible<br />
	if(((form.Left + form.Width - 60) < currentScreen.Bounds.Left) ||<br />
		((form.Left + 60) > (currentScreen.Bounds.Left + currentScreen.Bounds.Width))) <br />
	{<br />
		form.Left = currentScreen.Bounds.Left;<br />
	}<br />
	#endregion<br />
}<br />

GeneralRe: Multiple Screens/Monitors Pin
ejp1022-Feb-06 9:31
ejp1022-Feb-06 9:31 
GeneralRe: Multiple Screens/Monitors Pin
Christopher Scholten22-Feb-06 21:39
professionalChristopher Scholten22-Feb-06 21:39 
GeneralRe: Multiple Screens/Monitors Pin
Holm7623-Nov-06 3:07
Holm7623-Nov-06 3:07 
QuestionHow to use for initialization? Pin
Mark Jerde19-Jul-05 18:30
Mark Jerde19-Jul-05 18:30 
GeneralWindow setting saving extended with XML and custom saving Pin
angus_grant13-Aug-04 22:56
angus_grant13-Aug-04 22:56 
GeneralVB Version Pin
Pletzky1-Mar-04 17:13
Pletzky1-Mar-04 17:13 
GeneralForm is not sizable Pin
Tiberius DULUMAN27-Aug-03 5:48
Tiberius DULUMAN27-Aug-03 5:48 
Generalnew constructors Pin
donavon17-Jun-03 5:09
donavon17-Jun-03 5:09 
GeneralRe: new constructors Pin
Joel Matthias17-Jun-03 5:40
Joel Matthias17-Jun-03 5:40 
GeneralRe: new constructors Pin
Thomas Freudenberg9-Jul-03 8:38
Thomas Freudenberg9-Jul-03 8:38 
GeneralBetter way ... Pin
Wizard_0123-Jan-03 10:12
Wizard_0123-Jan-03 10:12 
GeneralRe: Better way ... Pin
Joel Matthias23-Jan-03 10:52
Joel Matthias23-Jan-03 10:52 
GeneralRe: Better way ... Pin
Wizard_0123-Jan-03 10:58
Wizard_0123-Jan-03 10:58 
GeneralRe: Better way ... Pin
Joel Matthias23-Jan-03 12:11
Joel Matthias23-Jan-03 12:11 
GeneralGreat Pin
hr1236-Dec-02 10:14
hr1236-Dec-02 10:14 
QuestionHow old is Joel? Pin
GeniusQ18-Sep-02 22:23
GeniusQ18-Sep-02 22:23 
AnswerRe: How old is Joel? Pin
Joel Matthias19-Sep-02 4:56
Joel Matthias19-Sep-02 4:56 
GeneralA Suggestion Pin
John Hurrell22-Mar-02 9:53
John Hurrell22-Mar-02 9:53 
GeneralRe: A Suggestion Pin
Luis Alonso Ramos14-Feb-03 14:23
Luis Alonso Ramos14-Feb-03 14:23 

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.