.NET
WPF
Window Size
Session Persistence
User Settings

.NET WPF Remember window size between sessions

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

When a user resizes your WPF window, arranges it on their second monitor, and then closes and reopens the application, they expect the window to appear exactly where they left it. Failing to persist window size and position is a small usability issue that creates a constant source of friction. WPF provides a built-in settings infrastructure through Properties.Settings that makes this straightforward to implement, requiring no external libraries or databases.

Why Properties.Settings Is the Right Tool

Before reaching for a JSON file or the Windows Registry, consider that .NET already includes a settings system designed specifically for this use case. Properties.Settings stores user-scoped values in a per-user XML file under AppData/Local, survives application updates, and requires no file I/O boilerplate. For window position data, this is the simplest and most idiomatic approach.

Creating the Settings

Open your project's Properties/Settings.settings file (or create one through Project Properties, then the Settings tab). Add four user-scoped settings:

NameTypeScopeDefault
WindowTopdoubleUser100
WindowLeftdoubleUser100
WindowHeightdoubleUser450
WindowWidthdoubleUser800

User scope is essential here. Application-scoped settings are read-only at runtime, so they cannot be saved.

Restoring Window State on Load

In your MainWindow.xaml.cs, handle the Window.Loaded event (or use the constructor after InitializeComponent) to restore the saved values:

csharp
1public partial class MainWindow : Window
2{
3    public MainWindow()
4    {
5        InitializeComponent();
6        Loaded += MainWindow_Loaded;
7        Closing += MainWindow_Closing;
8    }
9
10    private void MainWindow_Loaded(object sender, RoutedEventArgs e)
11    {
12        Top = Properties.Settings.Default.WindowTop;
13        Left = Properties.Settings.Default.WindowLeft;
14        Height = Properties.Settings.Default.WindowHeight;
15        Width = Properties.Settings.Default.WindowWidth;
16    }
17}

This sets the window's position and size to whatever was saved during the last session. On the very first run, the default values from the Settings file are used.

Saving Window State on Close

Handle the Window.Closing event to persist the current values before the application exits:

csharp
1private void MainWindow_Closing(object sender, System.ComponentModel.CancelEventArgs e)
2{
3    Properties.Settings.Default.WindowTop = Top;
4    Properties.Settings.Default.WindowLeft = Left;
5    Properties.Settings.Default.WindowHeight = Height;
6    Properties.Settings.Default.WindowWidth = Width;
7    Properties.Settings.Default.Save();
8}

The Save() call writes the values to the user's configuration file on disk. Without this call, the in-memory changes are discarded.

Using XAML Bindings Instead

If you prefer a declarative approach, you can bind window properties directly in XAML. First, add a namespace reference to your settings, then bind each property:

xml
1<Window x:Class="MyApp.MainWindow"
2        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
3        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
4        xmlns:p="clr-namespace:MyApp.Properties"
5        Top="{Binding Source={x:Static p:Settings.Default}, Path=WindowTop, Mode=TwoWay}"
6        Left="{Binding Source={x:Static p:Settings.Default}, Path=WindowLeft, Mode=TwoWay}"
7        Height="{Binding Source={x:Static p:Settings.Default}, Path=WindowHeight, Mode=TwoWay}"
8        Width="{Binding Source={x:Static p:Settings.Default}, Path=WindowWidth, Mode=TwoWay}">

With two-way bindings, the settings are updated automatically as the user resizes or moves the window. You still need to call Properties.Settings.Default.Save() in the Closing handler to persist to disk.

Handling Multi-Monitor Scenarios

A saved window position can become invalid if the user disconnects a monitor. Restoring the window to coordinates that are off-screen results in an invisible window. Add a validation step after restoring:

csharp
1private void MainWindow_Loaded(object sender, RoutedEventArgs e)
2{
3    Top = Properties.Settings.Default.WindowTop;
4    Left = Properties.Settings.Default.WindowLeft;
5    Height = Properties.Settings.Default.WindowHeight;
6    Width = Properties.Settings.Default.WindowWidth;
7
8    // Ensure the window is visible on at least one monitor
9    var windowRect = new System.Drawing.Rectangle(
10        (int)Left, (int)Top, (int)Width, (int)Height);
11
12    bool isVisible = System.Windows.Forms.Screen.AllScreens
13        .Any(screen => screen.WorkingArea.IntersectsWith(windowRect));
14
15    if (!isVisible)
16    {
17        Top = 100;
18        Left = 100;
19        Width = 800;
20        Height = 450;
21    }
22}

This checks whether the restored window rectangle intersects with any available screen. If not, it resets to safe default values. You need a reference to System.Windows.Forms for the Screen class.

Persisting Window State (Maximized, Normal)

Users also expect the maximized state to be remembered. Add a WindowState setting of type int (User scope, default 0) and save it alongside the position:

csharp
1private void MainWindow_Closing(object sender, System.ComponentModel.CancelEventArgs e)
2{
3    if (WindowState == WindowState.Normal)
4    {
5        Properties.Settings.Default.WindowTop = Top;
6        Properties.Settings.Default.WindowLeft = Left;
7        Properties.Settings.Default.WindowHeight = Height;
8        Properties.Settings.Default.WindowWidth = Width;
9    }
10    Properties.Settings.Default.WindowState = (int)WindowState;
11    Properties.Settings.Default.Save();
12}
13
14private void MainWindow_Loaded(object sender, RoutedEventArgs e)
15{
16    Top = Properties.Settings.Default.WindowTop;
17    Left = Properties.Settings.Default.WindowLeft;
18    Height = Properties.Settings.Default.WindowHeight;
19    Width = Properties.Settings.Default.WindowWidth;
20    WindowState = (WindowState)Properties.Settings.Default.WindowState;
21}

Note that size and position are only saved when the window is in the Normal state. If the window is maximized, Top, Left, Height, and Width reflect the maximized bounds, not the user's intended normal-state dimensions.

Alternative Approaches

While Properties.Settings works well for most cases, alternatives exist. For .NET Core/.NET 5+ projects where Properties.Settings may not be available, serialize a settings object to a JSON file using System.Text.Json. You can also store values in the Windows Registry under HKEY_CURRENT_USER, though this ties your application to Windows and complicates uninstall cleanup.

Common Pitfalls

  • Using Application scope instead of User scope: Application-scoped settings are read-only at runtime. Your Save() call will silently do nothing.
  • Forgetting to call Save(): In-memory changes are lost when the application exits. Always call Save() explicitly.
  • Not handling off-screen positions: A saved position from a disconnected monitor makes the window invisible on next launch.
  • Saving size while maximized: The Top, Left, Height, and Width values of a maximized window are the maximized bounds, not the restore bounds. Always check WindowState before saving dimensions.
  • Not handling first-run gracefully: Ensure your default settings in the Settings file produce a reasonable window size and position.

Summary

  • Use Properties.Settings.Default with User-scoped settings to persist window size and position between sessions.
  • Save on the Closing event and restore on the Loaded event, or use two-way XAML bindings.
  • Validate restored positions against available monitors to prevent invisible windows.
  • Save WindowState separately and only persist size/position when the window is in Normal state.
  • For .NET Core/.NET 5+ projects, consider a JSON file as an alternative to the traditional Properties.Settings mechanism.

Course illustration
Course illustration

All Rights Reserved.