.NET WPF Remember window size between sessions
Solution 1
Save the values in the user.config file.
You'll need to create the value in the settings file - it should be in the Properties folder. Create five values:
-
Top
of typedouble
-
Left
of typedouble
-
Height
of typedouble
-
Width
of typedouble
-
Maximized
of typebool
- to hold whether the window is maximized or not. If you want to store more information then a different type or structure will be needed.
Initialise the first two to 0 and the second two to the default size of your application, and the last one to false.
Create a Window_OnSourceInitialized event handler and add the following:
this.Top = Properties.Settings.Default.Top;
this.Left = Properties.Settings.Default.Left;
this.Height = Properties.Settings.Default.Height;
this.Width = Properties.Settings.Default.Width;
// Very quick and dirty - but it does the job
if (Properties.Settings.Default.Maximized)
{
WindowState = WindowState.Maximized;
}
NOTE: The set window placement needs to go in the on source initialised event of the window not the constructor, otherwise if you have the window maximised on a second monitor, it will always restart maximised on the primary monitor and you won't be able to access it.
Create a Window_Closing event handler and add the following:
if (WindowState == WindowState.Maximized)
{
// Use the RestoreBounds as the current values will be 0, 0 and the size of the screen
Properties.Settings.Default.Top = RestoreBounds.Top;
Properties.Settings.Default.Left = RestoreBounds.Left;
Properties.Settings.Default.Height = RestoreBounds.Height;
Properties.Settings.Default.Width = RestoreBounds.Width;
Properties.Settings.Default.Maximized = true;
}
else
{
Properties.Settings.Default.Top = this.Top;
Properties.Settings.Default.Left = this.Left;
Properties.Settings.Default.Height = this.Height;
Properties.Settings.Default.Width = this.Width;
Properties.Settings.Default.Maximized = false;
}
Properties.Settings.Default.Save();
This will fail if the user makes the display area smaller - either by disconnecting a screen or changing the screen resolution - while the application is closed so you should add a check that the desired location and size is still valid before applying the values.
Solution 2
Actually you don't need to use code-behind to do that (except for saving the settings). You can use a custom markup extension to bind the window size and position to the settings like this :
<Window x:Class="WpfApplication1.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:my="clr-namespace:WpfApplication1"
Title="Window1"
Height="{my:SettingBinding Height}"
Width="{my:SettingBinding Width}"
Left="{my:SettingBinding Left}"
Top="{my:SettingBinding Top}">
You can find the code for this markup extension here : http://www.thomaslevesque.com/2008/11/18/wpf-binding-to-application-settings-using-a-markup-extension/
Solution 3
While you can "roll your own" and manually save the settings somewhere, and in general it will work, it is very easy to not handle all of the cases correctly. It is much better to let the OS do the work for you, by calling GetWindowPlacement() at exit and SetWindowPlacement() at startup. It handles all of the crazy edge cases that can occur (multiple monitors, save the normal size of the window if it is closed while maximized, etc.) so that you don't have to.
This MSDN Sample shows how to use these with a WPF app. The sample isn't perfect (the window will start in the upper left corner as small as possible on first run, and there is some odd behavior with the Settings designer saving a value of type WINDOWPLACEMENT
), but it should at least get you started.
Solution 4
The "long form" binding that Thomas posted above requires almost no coding, just make sure you have the namespace binding:
<Window x:Class="WpfApplication1.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:p="clr-namespace:WpfApplication1.Properties"
Title="Window1"
Height="{Binding Source={x:Static p:Settings.Default}, Path=Height, Mode=TwoWay}"
Width="{Binding Source={x:Static p:Settings.Default}, Path=Width, Mode=TwoWay}"
Left="{Binding Source={x:Static p:Settings.Default}, Path=Left, Mode=TwoWay}"
Top="{Binding Source={x:Static p:Settings.Default}, Path=Top, Mode=TwoWay}">
Then to save on the code-behind:
private void frmMain_Closed(object sender, EventArgs e)
{
Properties.Settings.Default.Save();
}
Solution 5
Alternatively, you might like the following approach too (see source). Add the WindowSettings class to your project and insert WindowSettings.Save="True"
in your main window's header:
<Window x:Class="YOURPROJECT.Views.ShellView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:Services="clr-namespace:YOURNAMESPACE.Services"
Services:WindowSettings.Save="True">
Where WindowSettings is defined as follows:
using System;
using System.ComponentModel;
using System.Configuration;
using System.Windows;
namespace YOURNAMESPACE.Services
{
/// <summary>
/// Persists a Window's Size, Location and WindowState to UserScopeSettings
/// </summary>
public class WindowSettings
{
#region Fields
/// <summary>
/// Register the "Save" attached property and the "OnSaveInvalidated" callback
/// </summary>
public static readonly DependencyProperty SaveProperty = DependencyProperty.RegisterAttached("Save", typeof (bool), typeof (WindowSettings), new FrameworkPropertyMetadata(OnSaveInvalidated));
private readonly Window mWindow;
private WindowApplicationSettings mWindowApplicationSettings;
#endregion Fields
#region Constructors
public WindowSettings(Window pWindow) { mWindow = pWindow; }
#endregion Constructors
#region Properties
[Browsable(false)] public WindowApplicationSettings Settings {
get {
if (mWindowApplicationSettings == null) mWindowApplicationSettings = CreateWindowApplicationSettingsInstance();
return mWindowApplicationSettings;
}
}
#endregion Properties
#region Methods
public static void SetSave(DependencyObject pDependencyObject, bool pEnabled) { pDependencyObject.SetValue(SaveProperty, pEnabled); }
protected virtual WindowApplicationSettings CreateWindowApplicationSettingsInstance() { return new WindowApplicationSettings(this); }
/// <summary>
/// Load the Window Size Location and State from the settings object
/// </summary>
protected virtual void LoadWindowState() {
Settings.Reload();
if (Settings.Location != Rect.Empty) {
mWindow.Left = Settings.Location.Left;
mWindow.Top = Settings.Location.Top;
mWindow.Width = Settings.Location.Width;
mWindow.Height = Settings.Location.Height;
}
if (Settings.WindowState != WindowState.Maximized) mWindow.WindowState = Settings.WindowState;
}
/// <summary>
/// Save the Window Size, Location and State to the settings object
/// </summary>
protected virtual void SaveWindowState() {
Settings.WindowState = mWindow.WindowState;
Settings.Location = mWindow.RestoreBounds;
Settings.Save();
}
/// <summary>
/// Called when Save is changed on an object.
/// </summary>
private static void OnSaveInvalidated(DependencyObject pDependencyObject, DependencyPropertyChangedEventArgs pDependencyPropertyChangedEventArgs) {
var window = pDependencyObject as Window;
if (window != null)
if ((bool) pDependencyPropertyChangedEventArgs.NewValue) {
var settings = new WindowSettings(window);
settings.Attach();
}
}
private void Attach() {
if (mWindow != null) {
mWindow.Closing += WindowClosing;
mWindow.Initialized += WindowInitialized;
mWindow.Loaded += WindowLoaded;
}
}
private void WindowClosing(object pSender, CancelEventArgs pCancelEventArgs) { SaveWindowState(); }
private void WindowInitialized(object pSender, EventArgs pEventArgs) { LoadWindowState(); }
private void WindowLoaded(object pSender, RoutedEventArgs pRoutedEventArgs) { if (Settings.WindowState == WindowState.Maximized) mWindow.WindowState = Settings.WindowState; }
#endregion Methods
#region Nested Types
public class WindowApplicationSettings : ApplicationSettingsBase
{
#region Constructors
public WindowApplicationSettings(WindowSettings pWindowSettings) { }
#endregion Constructors
#region Properties
[UserScopedSetting] public Rect Location {
get {
if (this["Location"] != null) return ((Rect) this["Location"]);
return Rect.Empty;
}
set { this["Location"] = value; }
}
[UserScopedSetting] public WindowState WindowState {
get {
if (this["WindowState"] != null) return (WindowState) this["WindowState"];
return WindowState.Normal;
}
set { this["WindowState"] = value; }
}
#endregion Properties
}
#endregion Nested Types
}
}
Related videos on Youtube
Comments
-
Daniil Harik about 4 years
Basically when user resizes my application's window I want application to be same size when application is re-opened again.
At first I though of handling SizeChanged event and save Height and Width, but I think there must be easier solution.
Pretty simple problem, but I can not find easy solution to it.
-
Omer Raviv almost 12 yearsPlease note that if you're resoring both the size and the position (as most code samples below do), you'll want to handle the edge-case where someone unplugged the monitor that the window was last presented on, to avoid presenting your window off-screen.
-
Andrew Truckle almost 8 years@OmerRaviv Have you found an example taking the edge case in to account?
-
AelanY about 7 yearsI have too less repution to add a comment, hence I created this new awnser. I use the same solution as Lance Cleveland including the setting of RobJohnson, but it doesn't work if you use it for sub windows and want to open more of them at the same time...
-
-
aroon65 about 15 yearsWhat if the application is installed under Program Files and the user is running as non-admin?
-
ChrisF about 15 yearsSorry - I meant user.config which is stored under C:\Documents and Settings\[user]\Local Settings\Application Data where the [User] should have access rights. I'll update the answer.
-
Thomas Levesque about 15 yearsActually, settings with scope "User" are not saved in the app.config file in Program Files, but in a user.config file in the user's application data directory. So it's not a problem...
-
ChrisF about 15 yearsI realised the mistake in the original answer when I saw Kent's comment and corrected it - probably at the same time you were adding your comment!
-
longaster over 14 yearsI like this answer more than the chosen accepted answer. Well done.
-
MartyIX almost 14 yearsActually you can add "WindowState" to settings. Select type -> browse -> PresentationFramework -> System.Windows -> WindowState :)
-
Thomas over 13 yearsFWIW, I do this from the size changed handler as well, in case of application crashes. They're rare with an unhandled exception processing, but why punish the user with lost size/location when they do mysteriously occur.
-
Matt DeKrey over 13 years+1 - I love the use of binding and extensions! If you add the WindowState to your bound settings, it provides the full capabilities. Alternatively, if you have the user settings available in the DataContext, you can use something like
{Binding Settings.Height}
, etc. -
David Sykes over 12 yearsI chose this solution, but only saved the settings if the window state was normal, otherwise it can be fiddly getting it out of maximised mode
-
Menefee over 12 years@MartyIX WindowState only manages maximize, minimized, or restored and does not address the size of window at all so I don't see how it is at all relavent.
-
Omer Raviv almost 12 yearsThere's a bug in this code in that, if the user opens the window on his/her second screen, then disconnects that screen from the computer, the next time they open the window, it will be presented off screen. If the window is modal, the user won't be able to interact with the app at all, and won't understand what's going on. You need to add a bounds check using Window.GetScreen(), after converting the screen coordinates to DPI dependant values.
-
ChrisF almost 12 years@OmerRaviv - it's not a bug, but a limitation :) Seriously - I didn't address that aspect of the problem.
-
RobJohnson over 11 years+1 I used this too, @DavidSykes - Adding another setting for the window state seems to work well enough, e.g.
WindowState="{Binding Source={x:Static properties:Settings.Default}, Path=WindowState, Mode=TwoWay}"
-
David Sykes about 11 years@RobJohnson I tried your suggestion and it worked very well, thanks.
-
Vinicius Rocha over 10 yearsThis approach have an issue when the user close the application when the Window is Maximized.
-
Thomas Levesque over 10 years@Vinicius, can you elaborate? What is the issue exactly?
-
Vinicius Rocha over 10 yearsHi @ThomasLevesque. When you maximize the window the values of width, height, left and top changes. So, if you close the application and then reopen it, the application wont remember the width/height/top/left from when it was in normal mode.
-
Rafael Rivera about 10 yearsLet the OS do the heavy lifting... (see Andy's answer)
-
Gleb Sevruk almost 10 yearswindow.Intitialized should be window.Loaded see mostlytech.blogspot.com/2008/01/…
-
tster almost 10 years@Gleb, both work I think. Are you having problems with it on Initialized?
-
Gleb Sevruk almost 10 yearsYes, since maximized window will be on incorrect screen if you use only initialized event. What I've done and this seems to work: Now I'm subscribing to Loaded event also. I moved _window.WindowState = s.Maximized ? WindowState.Maximized : WindowState.Normal; line inside "Loaded" event handler. window.Initialized += InitializedHandler; window.Loaded += LoadedHandler; btw: I like this approach
-
PIntag over 9 years@Vinicius: I don't seem to have a problem with the app properly remembering width/height/top/left setting when closing the app while maximized.
-
Alan Baljeu about 9 yearsNot working. If maximized on screen 2 when closed, it opens maximized on screen 1 when started.
-
ChrisF about 9 years@AlanBaljeu - you'll have to extend this to save which screen the application was closed on.
-
Piyush Soni about 9 yearsBut doesn't it still require us to write code behind (perhaps even more?) by writing a whole new extension/class?
-
Mark Bell over 8 yearsNice solution. However I just discovered that GetWindowPlacement/SetWindowPlacement are not Aero Snap aware
-
Andrew Truckle almost 8 yearsWhat about when people have two monitors and thus it might have negative coordinates and then they change monitor configurations and the values are no longer valid?
-
CptObvious almost 7 yearsNice, thanks for this - I've used your code snippet in a new class to set up the state tracker with a path based on the program's name. From now on I only have to write one line and all the window properties are handled
-
KnorxThieus over 6 yearsBy the way, you must indeed not subscribe to
Window_Closed
instead ofWindow_Closing
, although this might lead to a better user experience. For if you queryRestoreBounds
before the window has been shown or after it has been closed,Empty
is returned., like MSDN says. -
Kyle Delaney over 6 yearsTo be clear, this works with both application and user settings, right? The link only says application settings.
-
Kyle Delaney over 6 yearsSince File Explorer displays this behavior, I was wondering if there's some automatic way of getting a window to do this, but I guess not.
-
Stéphane Gourichon about 6 years@RandomEngy has posted an improved answer based on this.
-
Apfelkuacha over 5 years
RestoreBounds
could also be used when the Window isn't maximized, so the if-else is not necessary -
zar over 3 yearsI downloaded your sample from blog and that works and I am doing identical in a new application and somehow that doesn't work. Any idea if we need to do something more?
-
曾其威 almost 3 yearsI save on
Settings.Default.PropertyChanged += (s, e) => Settings.Default.Save()
, and add some delayWindowState="{Binding MainWindow_WindowState, Source={x:Static properties:Settings.Default}, Delay=250, Mode=TwoWay}"
so it saves some SSD. -
Urs Meili over 2 yearsthis package works great, and it's also handling minimized windows and disconnected monitors correctly. IMHO This should be the accepted answer.
-
Matt Varblow about 2 yearsDefinitely check out the github page. This solution includes support for multiple monitors: github.com/anakic/Jot#real-world-formwindow-tracking
-
Duncan Groenewald about 2 yearsBest one yet - just need to call RestoreSizeAndLocation() from MainWindow() and after InitialiseComponent() otherwise there is a visible jump.