Remembering window positions in WPF

On a WPF app we had the need for the application to remember its window position and state (maximized, minimized) across restarts. A short journey on the Internet led me to an interesting article by TimK on CodeProject on how to do this.


As you may know, Windows Presentation Foundation makes heavy use of XAML. As it turns out XAML also allows you to mix-in behavior with your classes without using code. That is a productive way of reusing behavior. Behind the screens the mix-in class uses WPF magic as DependencyProperties to wire things up.


The article contains a WindowSettings class that stores and restores the window position and state. I fixed one bug in this WindowSettings class from TimK. Without the fix an app would restore maximized on the wrong display if it was maximized on a secondary display.


You can find the C# code for our WindowSettings class attached to this post. For your reading convenience I have also copied the code below.


To use it in your WPF window defined in XAML, do something like:

<Window
xmlns=”http://schemas.microsoft.com/winfx/2006/xaml/presentation”
xmlns:x=”http://schemas.microsoft.com/winfx/2006/xaml”
x:Class=”LogicaCMG.Client.MainWindow”
Title=”Test Window” Height=”700″ Width=”880″
xmlns:settings=”clr-namespace:LogicaCMG.Settings”
settings:WindowSettings.Save=”True”
>

Code of the WindowSettings class:

using System;
using System.Diagnostics;
using System.Globalization;
using System.ComponentModel;
using System.Configuration;
using System.Windows;
using System.Windows.Markup;

namespace LogicaCMG.Settings
{
/// <summary>
/// Persists a Window’s Size, Location and WindowState to UserScopeSettings
/// </summary>
public class WindowSettings
{
#region WindowApplicationSettings Helper Class
public class WindowApplicationSettings : ApplicationSettingsBase
{
private WindowSettings windowSettings;

public WindowApplicationSettings(WindowSettings windowSettings)
: base(windowSettings.window.PersistId.ToString())
{
this.windowSettings = windowSettings;
}

[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

#region Constructor
private Window window = null;

public WindowSettings(Window window)
{
this.window = window;
}

#endregion

#region Attached “Save” Property Implementation
/// <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(new PropertyChangedCallback(OnSaveInvalidated)));

public static void SetSave(DependencyObject dependencyObject, bool enabled)
{
dependencyObject.SetValue(SaveProperty, enabled);
}

/// <summary>
/// Called when Save is changed on an object.
/// </summary>
private static void OnSaveInvalidated(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
{
Window window = dependencyObject as Window;
if (window != null)
{
if ((bool)e.NewValue)
{
WindowSettings settings = new WindowSettings(window);
settings.Attach();
}
}
}

#endregion

#region Protected Methods
/// <summary>
/// Load the Window Size Location and State from the settings object
/// </summary>
protected virtual void LoadWindowState()
{
this.Settings.Reload();
if (this.Settings.Location != Rect.Empty)
{
this.window.Left = this.Settings.Location.Left;
this.window.Top = this.Settings.Location.Top;
this.window.Width = this.Settings.Location.Width;
this.window.Height = this.Settings.Location.Height;
}

if (this.Settings.WindowState != WindowState.Maximized)
{
this.window.WindowState = this.Settings.WindowState;
}
}

/// <summary>
/// Save the Window Size, Location and State to the settings object
/// </summary>
protected virtual void SaveWindowState()
{
this.Settings.WindowState = this.window.WindowState;
this.Settings.Location = this.window.RestoreBounds;
this.Settings.Save();
}
#endregion

#region Private Methods

private void Attach()
{
if (this.window != null)
{
this.window.Closing += new CancelEventHandler(window_Closing);
this.window.Initialized += new EventHandler(window_Initialized);
this.window.Loaded += new RoutedEventHandler(window_Loaded);
}
}

private void window_Loaded(object sender, RoutedEventArgs e)
{
if (this.Settings.WindowState == WindowState.Maximized)
{
this.window.WindowState = this.Settings.WindowState;
}
}

private void window_Initialized(object sender, EventArgs e)
{
LoadWindowState();
}

private void window_Closing(object sender, CancelEventArgs e)
{
SaveWindowState();
}
#endregion

#region Settings Property Implementation
private WindowApplicationSettings windowApplicationSettings = null;

protected virtual WindowApplicationSettings CreateWindowApplicationSettingsInstance()
{
return new WindowApplicationSettings(this);
}

[Browsable(false)]
public WindowApplicationSettings Settings
{
get
{
if (windowApplicationSettings == null)
{
this.windowApplicationSettings = CreateWindowApplicationSettingsInstance();
}
return this.windowApplicationSettings;
}
}
#endregion
}
}

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>