Restoring Window Size/Position With Multiple Monitors

Michael Sorens picture Michael Sorens · Jun 2, 2009 · Viewed 39.6k times · Source

Many posts around about restoring a WinForm position and size.

Examples:

But I have yet to find code to do this with multiple monitors.

That is, if I close my .NET Winform app with the window on monitor 2, I want it to save the windows size, location, and state to the application settings, so it could later restore to monitor 2 when I restart the app. It would be nice if, like in the codeproject example above, it includes some sanity checks, as in if the saved location is mostly off-screen it "fixes" it. Or if the saved location is on a monitor that is no longer there (e.g. my laptop is now by itself without my second monitor) then it correctly moves it to monitor 1.

Any thoughts?

My environment: C#, .NET 3.5 or below, VS2008

Answer

VVS picture VVS · Jun 2, 2009

Try this code. Points of interest:

  • Checks if the window is (partially) visible on any screen's working area. E.g. dragging it behind the task bar or moving it completely offscreen resets the position to windows default.
  • Saves the correct bounds even if the Form is minimized or maximized (common error)
  • Saves the WindowState correctly. Saving FormWindowState.Minimized is disabled by design.

The bounds and state are stored in the appsettings with their corresponding type so there's no need to do any string parsing. Let the framework do its serialization magic.

public partial class MainForm : Form
{
    public MainForm()
    {
        InitializeComponent();

        // this is the default
        this.WindowState = FormWindowState.Normal;
        this.StartPosition = FormStartPosition.WindowsDefaultBounds;

        // check if the saved bounds are nonzero and visible on any screen
        if (Settings.Default.WindowPosition != Rectangle.Empty &&
            IsVisibleOnAnyScreen(Settings.Default.WindowPosition))
        {
            // first set the bounds
            this.StartPosition = FormStartPosition.Manual;
            this.DesktopBounds = Settings.Default.WindowPosition;

            // afterwards set the window state to the saved value (which could be Maximized)
            this.WindowState = Settings.Default.WindowState;
        }
        else
        {
            // this resets the upper left corner of the window to windows standards
            this.StartPosition = FormStartPosition.WindowsDefaultLocation;

            // we can still apply the saved size
            this.Size = Settings.Default.WindowPosition.Size;
        }
    }

    private bool IsVisibleOnAnyScreen(Rectangle rect)
    {
        foreach (Screen screen in Screen.AllScreens)
        {
            if (screen.WorkingArea.IntersectsWith(rect))
            {
                return true;
            }
        }

        return false;
    }

    protected override void OnClosed(EventArgs e)
    {
        base.OnClosed(e);

        // only save the WindowState if Normal or Maximized
        switch (this.WindowState)
        {
            case FormWindowState.Normal:
            case FormWindowState.Maximized:
                Settings.Default.WindowState = this.WindowState;
                break;

            default:
                Settings.Default.WindowState = FormWindowState.Normal;
                break;
        }

        // reset window state to normal to get the correct bounds
        // also make the form invisible to prevent distracting the user
        this.Visible = false;
        this.WindowState = FormWindowState.Normal;

        Settings.Default.WindowPosition = this.DesktopBounds;
        Settings.Default.Save();
    }
}

The settings file for reference:

<?xml version='1.0' encoding='utf-8'?>
<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)" GeneratedClassNamespace="ScreenTest" GeneratedClassName="Settings">
    <Profiles />
    <Settings>
        <Setting Name="WindowPosition" Type="System.Drawing.Rectangle" Scope="User">
            <Value Profile="(Default)">0, 0, 0, 0</Value>
        </Setting>
        <Setting Name="WindowState" Type="System.Windows.Forms.FormWindowState" Scope="User">
            <Value Profile="(Default)">Normal</Value>
        </Setting>
    </Settings>
</SettingsFile>