.NET / Windows Forms: remember windows size and location

C#.NetWinformsWindow

C# Problem Overview


I have a Windows Forms application with a normal window. Now when I close the application and restart it, I want that the main window appears at the same location on my screen with the same size of the moment when it was closed.

Is there an easy way in Windows Forms to remember the screen location and window size (and if possible the window state) or does everything have to be done by hand?

C# Solutions


Solution 1 - C#

If you add this code to your FormClosing event handler:

if (WindowState == FormWindowState.Maximized)
{
    Properties.Settings.Default.Location = RestoreBounds.Location;
    Properties.Settings.Default.Size = RestoreBounds.Size;
    Properties.Settings.Default.Maximised = true;
    Properties.Settings.Default.Minimised = false;
}
else if (WindowState == FormWindowState.Normal)
{
    Properties.Settings.Default.Location = Location;
    Properties.Settings.Default.Size = Size;
    Properties.Settings.Default.Maximised = false;
    Properties.Settings.Default.Minimised = false;
}
else
{
    Properties.Settings.Default.Location = RestoreBounds.Location;
    Properties.Settings.Default.Size = RestoreBounds.Size;
    Properties.Settings.Default.Maximised = false;
    Properties.Settings.Default.Minimised = true;
}
Properties.Settings.Default.Save();

It will save the current state.

Then add this code to your form's OnLoad handler:

if (Properties.Settings.Default.Maximised)
{
	Location = Properties.Settings.Default.Location;
	WindowState = FormWindowState.Maximized;
	Size = Properties.Settings.Default.Size;
}
else if (Properties.Settings.Default.Minimised)
{
	Location = Properties.Settings.Default.Location;
	WindowState = FormWindowState.Minimized;
	Size = Properties.Settings.Default.Size;
}
else
{
	Location = Properties.Settings.Default.Location;
	Size = Properties.Settings.Default.Size;
}

It will restore the last state.

It even remembers which monitor in a multi monitor set up the application was maximised to.

Solution 2 - C#

You'll need to save the window location and size in your application settings. Here's a good C# article to show you how.

EDIT

You can save pretty much anything you want in the application settings. In the Type column of the settings grid you can browse to any .NET type. WindowState is in System.Windows.Forms and is listed as FormWindowState. There's also a property for FormStartPosition.

Solution 3 - C#

Previous solutions didn't work for me. After playing a while I ended up with following code which:

  • preserves maximised and normal state

  • replaces minimised state with default position

  • in case of screen size changes (detached monitor, remote connection,...) it will not get user into frustrating state with application open outside of screen.

     private void MyForm_Load(object sender, EventArgs e)
     {
         if (Properties.Settings.Default.IsMaximized)
             WindowState = FormWindowState.Maximized;
         else if (Screen.AllScreens.Any(screen => screen.WorkingArea.IntersectsWith(Properties.Settings.Default.WindowPosition)))
         {
             StartPosition = FormStartPosition.Manual;
             DesktopBounds = Properties.Settings.Default.WindowPosition;
             WindowState = FormWindowState.Normal;
         }
     }
    
     private void MyForm_FormClosing(object sender, FormClosingEventArgs e)
     {
         Properties.Settings.Default.IsMaximized = WindowState == FormWindowState.Maximized;
         Properties.Settings.Default.WindowPosition = DesktopBounds;
         Properties.Settings.Default.Save();
     }
    

user settings:

    <userSettings>
    <WindowsFormsApplication2.Properties.Settings>
        <setting name="WindowPosition" serializeAs="String">
            <value>0, 0, -1, -1</value>
        </setting>
        <setting name="IsMaximized" serializeAs="String">
            <value>False</value>
        </setting>
    </WindowsFormsApplication2.Properties.Settings>
</userSettings>

Note: the WindowsPosition is intentionally wrong, so during first launch application will use default location.

Note that IntersectsWith expects a Rectangle, not a Point. So unlike other answers, this answer is saving the DesktopBounds, not Location, into Properties.Settings.Default.WindowPosition

Solution 4 - C#

I tried a few different methods; this is what ended up working for me. (In this case - on first launch - the defaults haven't been persisted yet, so the form will use the values set in the designer)

  1. Add the settings to the project (manually - don't rely on visual studio): Properties.Settings
  1. Add the following code to your form:

     private void Form1_Load(object sender, EventArgs e)
     {
         this.RestoreWindowPosition();
     }
    
     private void Form1_FormClosing(object sender, FormClosingEventArgs e)
     {
         this.SaveWindowPosition();
     }
    
     private void RestoreWindowPosition()
     {
         if (Settings.Default.HasSetDefaults)
         {
             this.WindowState = Settings.Default.WindowState;
             this.Location = Settings.Default.Location;
             this.Size = Settings.Default.Size;
         }
     }
    
     private void SaveWindowPosition()
     {
         Settings.Default.WindowState = this.WindowState;
    
         if (this.WindowState == FormWindowState.Normal)
         {
             Settings.Default.Location = this.Location;
             Settings.Default.Size = this.Size;
         }
         else
         {
             Settings.Default.Location = this.RestoreBounds.Location;
             Settings.Default.Size = this.RestoreBounds.Size;
         }
    
         Settings.Default.HasSetDefaults = true;
    
         Settings.Default.Save();
     }
    

Solution 5 - C#

If you use the fabulous open source library - Jot, you can forget about the tedious .settings files and just do this:

public MainWindow()
{
    InitializeComponent();

    _stateTracker.Configure(this)
        .IdentifyAs("MyMainWindow")
        .AddProperties(nameof(Height), nameof(Width), nameof(Left), nameof(Top), nameof(WindowState))
        .RegisterPersistTrigger(nameof(Closed))
        .Apply();
}

There's a Nuget package as well, and you can configure pretty much everything about how/when/where data is stored.

Disclaimer: I'm the author, but the library is completely open source (under MIT license).

Solution 6 - C#

Matt - to save the WindowState as a user setting, in the Settings Dialog, in the "Type" dropdown, scroll to the bottom and select "Browse".

In the "Select a Type" dialog, expand System.Windows.Forms and you can choose "FormWindowState" as the type.

(sorry, I don't see a button that allows me to comment on the comment...)

Solution 7 - C#

If you have more than 1 form you can use something like this...

Add this part all form load void

var AbbA = Program.LoadFormLocationAndSize(this);
            this.Location = new Point(AbbA[0], AbbA[1]);
            this.Size = new Size(AbbA[2], AbbA[3]);
            this.FormClosing += new FormClosingEventHandler(Program.SaveFormLocationAndSize);

Save form location and size to app.config xml

public static void SaveFormLocationAndSize(object sender, FormClosingEventArgs e)
{
    Form xForm = sender as Form;
    Configuration config = ConfigurationManager.OpenExeConfiguration(Application.ExecutablePath);
    if (ConfigurationManager.AppSettings.AllKeys.Contains(xForm.Name))
        config.AppSettings.Settings[xForm.Name].Value = String.Format("{0};{1};{2};{3}", xForm.Location.X, xForm.Location.Y, xForm.Size.Width, xForm.Size.Height);
    else
        config.AppSettings.Settings.Add(xForm.Name, String.Format("{0};{1};{2};{3}", xForm.Location.X, xForm.Location.Y, xForm.Size.Width, xForm.Size.Height));
    config.Save(ConfigurationSaveMode.Full);
}

Load form location and size from app.config xml

public static int[] LoadFormLocationAndSize(Form xForm)
{
    int[] LocationAndSize = new int[] { xForm.Location.X, xForm.Location.Y, xForm.Size.Width, xForm.Size.Height };
    //---//
    try
    {
        Configuration config = ConfigurationManager.OpenExeConfiguration(Application.ExecutablePath);
        var AbbA = config.AppSettings.Settings[xForm.Name].Value.Split(';');
        //---//
        LocationAndSize[0] = Convert.ToInt32(AbbA.GetValue(0));
        LocationAndSize[1] = Convert.ToInt32(AbbA.GetValue(1));
        LocationAndSize[2] = Convert.ToInt32(AbbA.GetValue(2));
        LocationAndSize[3] = Convert.ToInt32(AbbA.GetValue(3));
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message);
    }
    //---//
    return LocationAndSize;
}

Solution 8 - C#

You'll have to manually save the information somewhere. I'd suggest doing so as application settings, storing them in user specific isolated storage.

Once you load up, read the settings then resize/move your form.

Solution 9 - C#

My answer is adapted from ChrisF♦'s answer, but I've fixed one thing I didn't like - if the window is minimized at the time of closing, it would appear minimized on next start.

My code handles that case correctly by remembering whether the window was maximized or normal at the time of its minimization, and setting the persistent state accordingly.

Unfortunately, Winforms doesn't expose that information directly, so I needed to override WndProc and store it myself. See https://stackoverflow.com/questions/39365818/check-if-currently-minimized-window-was-in-maximized-or-normal-state-at-the-time

partial class Form1 : Form
{
	protected override void WndProc(ref Message m)
	{
		if (m.Msg == WM_SYSCOMMAND)
		{
			int wparam = m.WParam.ToInt32() & 0xfff0;

			if (wparam == SC_MAXIMIZE)
				LastWindowState = FormWindowState.Maximized;
			else if (wparam == SC_RESTORE)
				LastWindowState = FormWindowState.Normal;
		}

		base.WndProc(ref m);
	}

	private const int WM_SYSCOMMAND = 0x0112;
	private const int SC_MAXIMIZE = 0xf030;
	private const int SC_RESTORE = 0xf120;
	private FormWindowState LastWindowState;

	private void Form1_FormClosing(object sender, FormClosingEventArgs e)
	{
		if (WindowState == FormWindowState.Normal)
		{
			Properties.Settings.Default.WindowLocation = Location;
			Properties.Settings.Default.WindowSize = Size;
		}
		else
		{
			Properties.Settings.Default.WindowLocation = RestoreBounds.Location;
			Properties.Settings.Default.WindowSize = RestoreBounds.Size;
		}

		if (WindowState == FormWindowState.Minimized)
		{
			Properties.Settings.Default.WindowState = LastWindowState;
		}
		else
		{
			Properties.Settings.Default.WindowState = WindowState;
		}

		Properties.Settings.Default.Save();
	}

	private void Form1_Load(object sender, EventArgs e)
	{
		if (Properties.Settings.Default.WindowSize != new Size(0, 0))
		{
			Location = Properties.Settings.Default.WindowLocation;
			Size = Properties.Settings.Default.WindowSize;
			WindowState = Properties.Settings.Default.WindowState;
		}
	}

Solution 10 - C#

You could also save it in your (let's say) config.xml when you close the form:

private void Form1_FormClosed(object sender, FormClosedEventArgs e)
{
	XmlDocument docConfigPath = new XmlDocument();
	docConfigPath.Load(XML_Config_Path);

	WriteNode(new string[] { "config", "Size", "Top", Top.ToString() }, docConfigPath);
	WriteNode(new string[] { "config", "Size", "Left", Left.ToString() }, docConfigPath);
	WriteNode(new string[] { "config", "Size", "Height", Height.ToString() }, docConfigPath);
	WriteNode(new string[] { "config", "Size", "Width", Width.ToString() }, docConfigPath);

	docConfigPath.Save(XML_Config_Path);
}

public static XmlNode WriteNode(string[] sNode, XmlDocument docConfigPath)
{
	int cnt = sNode.Length;
	int iNode = 0;
	string sNodeNameLast = "/" + sNode[0];
	string sNodeName = "";
	XmlNode[] xN = new XmlNode[cnt];

	for (iNode = 1; iNode < cnt - 1; iNode++)
	{
		sNodeName = "/" + sNode[iNode];
		xN[iNode] = docConfigPath.SelectSingleNode(sNodeNameLast + sNodeName);
		if (xN[iNode] == null)
		{
			xN[iNode] = docConfigPath.CreateNode("element", sNode[iNode], "");
			xN[iNode].InnerText = "";
			docConfigPath.SelectSingleNode(sNodeNameLast).AppendChild(xN[iNode]);
		}
		sNodeNameLast += sNodeName;
	}

	if (sNode[cnt - 1] != "")
		xN[iNode - 1].InnerText = sNode[cnt - 1];

	return xN[cnt - 2];
}

And the loading is on your:

private void Form1_Load(object sender, EventArgs e)
{
    XmlDocument docConfigPath = new XmlDocument();
	docConfigPath.Load(XML_Config_Path);
	XmlNodeList nodeList = docConfigPath.SelectNodes("config/Size");

	Height = ReadNodeInnerTextAsNumber("config/Size/Height", docConfigPath);
	Width = ReadNodeInnerTextAsNumber("config/Size/Width", docConfigPath);
	Top = ReadNodeInnerTextAsNumber("config/Size/Top", docConfigPath);
	Left = ReadNodeInnerTextAsNumber("config/Size/Left", docConfigPath);
}

The config.xml should contain the following:

<?xml version="1.0" encoding="utf-8"?>
<config>
  <Size>
    <Height>800</Height>
    <Width>1400</Width>
    <Top>100</Top>
    <Left>280</Left>
  </Size>
</config>

Solution 11 - C#

I've been using this method so far and it's been working great. You don't have to fiddle around with application settings. Instead, it uses serialization to write a settings file to your working directory. I use JSON, but you can use .NET's native XML serialization or any serialization for that matter.

Put these static methods in a common extensions class. Bonus points if you have a common extensions project that you reference by multiple projects:

const string WINDOW_STATE_FILE = "windowstate.json";

public static void SaveWindowState(Form form)
{
    var state = new WindowStateInfo
    {
        WindowLocation = form.Location,
        WindowState = form.WindowState
    };

    File.WriteAllText(WINDOW_STATE_FILE, JsonConvert.SerializeObject(state));
}

public static void LoadWindowState(Form form)
{
    if (!File.Exists(WINDOW_STATE_FILE)) return;

    var state = JsonConvert.DeserializeObject<WindowStateInfo>(File.ReadAllText(WINDOW_STATE_FILE));

    if (state.WindowState.HasValue) form.WindowState = state.WindowState.Value;
    if (state.WindowLocation.HasValue) form.Location = state.WindowLocation.Value;
}

public class WindowStateInfo
{
    public FormWindowState? WindowState { get; set; }

    public Point? WindowLocation { get; set; }
}

You only need to write that code once and never mess with again. Now for the fun part: Put the below code in your form's Load and FormClosing events like so:

private void Form1_Load(object sender, EventArgs e)
{
    WinFormsGeneralExtensions.LoadWindowState(this);
}

private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
    WinFormsGeneralExtensions.SaveWindowState(this);
}

That is all you need to do. The only setup is getting those extensions into a common class. After that, just add two lines of code to your form's code and you're done.

This code will only really work if your WinForm's app has a single form. If it has multiple forms that you want to remember the positions of, you'll need to get creative and do something like this:

public static void SaveWindowState(Form form)
{
    var state = new WindowStateInfo
    {
        WindowLocation = form.Location,
        WindowState = form.WindowState
    };

    File.WriteAllText($"{form.Name} {WINDOW_STATE_FILE}", JsonConvert.SerializeObject(state));
}

I only save location and state, but you can modify this to remember form height and width or anything else. Just make the change once and it will work for any application that calls it.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionclampView Question on Stackoverflow
Solution 1 - C#ChrisFView Answer on Stackoverflow
Solution 2 - C#WalterView Answer on Stackoverflow
Solution 3 - C#jingView Answer on Stackoverflow
Solution 4 - C#darksider474View Answer on Stackoverflow
Solution 5 - C#anakicView Answer on Stackoverflow
Solution 6 - C#JohnForDummiesView Answer on Stackoverflow
Solution 7 - C#user1751206View Answer on Stackoverflow
Solution 8 - C#IanView Answer on Stackoverflow
Solution 9 - C#sashoalmView Answer on Stackoverflow
Solution 10 - C#DenizView Answer on Stackoverflow
Solution 11 - C#oscilatingcretinView Answer on Stackoverflow