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 SizeChange
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
}
}
I made a more generic solution based on RandomEngys brilliant answer. It saves the position to file in the running folder and you don't need to create new properties for each new window you create. This sollution works great for me with minimal code in code behind.
using System;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows;
using System.Windows.Interop;
using System.Xml;
using System.Xml.Serialization;
namespace WindowPlacementNameSpace
{
// RECT structure required by WINDOWPLACEMENT structure
[Serializable]
[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
public int Left;
public int Top;
public int Right;
public int Bottom;
public RECT(int left, int top, int right, int bottom)
{
this.Left = left;
this.Top = top;
this.Right = right;
this.Bottom = bottom;
}
}
// POINT structure required by WINDOWPLACEMENT structure
[Serializable]
[StructLayout(LayoutKind.Sequential)]
public struct POINT
{
public int X;
public int Y;
public POINT(int x, int y)
{
this.X = x;
this.Y = y;
}
}
// WINDOWPLACEMENT stores the position, size, and state of a window
[Serializable]
[StructLayout(LayoutKind.Sequential)]
public struct WINDOWPLACEMENT
{
public int length;
public int flags;
public int showCmd;
public POINT minPosition;
public POINT maxPosition;
public RECT normalPosition;
}
public static class WindowPlacement
{
private static readonly Encoding Encoding = new UTF8Encoding();
private static readonly XmlSerializer Serializer = new XmlSerializer(typeof(WINDOWPLACEMENT));
[DllImport("user32.dll")]
private static extern bool SetWindowPlacement(IntPtr hWnd, [In] ref WINDOWPLACEMENT lpwndpl);
[DllImport("user32.dll")]
private static extern bool GetWindowPlacement(IntPtr hWnd, out WINDOWPLACEMENT lpwndpl);
private const int SW_SHOWNORMAL = 1;
private const int SW_SHOWMINIMIZED = 2;
private static void SetPlacement(IntPtr windowHandle, string placementXml)
{
if (string.IsNullOrEmpty(placementXml))
{
return;
}
byte[] xmlBytes = Encoding.GetBytes(placementXml);
try
{
WINDOWPLACEMENT placement;
using (MemoryStream memoryStream = new MemoryStream(xmlBytes))
{
placement = (WINDOWPLACEMENT)Serializer.Deserialize(memoryStream);
}
placement.length = Marshal.SizeOf(typeof(WINDOWPLACEMENT));
placement.flags = 0;
placement.showCmd = (placement.showCmd == SW_SHOWMINIMIZED ? SW_SHOWNORMAL : placement.showCmd);
SetWindowPlacement(windowHandle, ref placement);
}
catch (InvalidOperationException)
{
// Parsing placement XML failed. Fail silently.
}
}
private static string GetPlacement(IntPtr windowHandle)
{
WINDOWPLACEMENT placement;
GetWindowPlacement(windowHandle, out placement);
using (MemoryStream memoryStream = new MemoryStream())
{
using (XmlTextWriter xmlTextWriter = new XmlTextWriter(memoryStream, Encoding.UTF8))
{
Serializer.Serialize(xmlTextWriter, placement);
byte[] xmlBytes = memoryStream.ToArray();
return Encoding.GetString(xmlBytes);
}
}
}
public static void ApplyPlacement(this Window window)
{
var className = window.GetType().Name;
try
{
var pos = File.ReadAllText(Directory + "\\" + className + ".pos");
SetPlacement(new WindowInteropHelper(window).Handle, pos);
}
catch (Exception exception)
{
Log.Error("Couldn't read position for " + className, exception);
}
}
public static void SavePlacement(this Window window)
{
var className = window.GetType().Name;
var pos = GetPlacement(new WindowInteropHelper(window).Handle);
try
{
File.WriteAllText(Directory + "\\" + className + ".pos", pos);
}
catch (Exception exception)
{
Log.Error("Couldn't write position for " + className, exception);
}
}
private static string Directory => Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
}
}
In your code behind you add these two methods
///This method is save the actual position of the window to file "WindowName.pos"
private void ClosingTrigger(object sender, EventArgs e)
{
this.SavePlacement();
}
///This method is load the actual position of the window from the file
protected override void OnSourceInitialized(EventArgs e)
{
base.OnSourceInitialized(e);
this.ApplyPlacement();
}
in the xaml window you add this
Closing="ClosingTrigger"
I'm using the answer from Lance Cleveland and bind the Setting. But i'm using some more Code to avoid that my Window is out of Screen.
private void SetWindowSettingsIntoScreenArea()
{
// first detect Screen, where we will display the Window
// second correct bottom and right position
// then the top and left position.
// If Size is bigger than current Screen, it's still possible to move and size the Window
// get the screen to display the window
var screen = System.Windows.Forms.Screen.FromPoint(new System.Drawing.Point((int)Default.Left, (int)Default.Top));
// is bottom position out of screen for more than 1/3 Height of Window?
if (Default.Top + (Default.Height / 3) > screen.WorkingArea.Height)
Default.Top = screen.WorkingArea.Height - Default.Height;
// is right position out of screen for more than 1/2 Width of Window?
if (Default.Left + (Default.Width / 2) > screen.WorkingArea.Width)
Default.Left = screen.WorkingArea.Width - Default.Width;
// is top position out of screen?
if (Default.Top < screen.WorkingArea.Top)
Default.Top = screen.WorkingArea.Top;
// is left position out of screen?
if (Default.Left < screen.WorkingArea.Left)
Default.Left = screen.WorkingArea.Left;
}
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();
}
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/
Create a string named WindowXml in your default Settings.
Use this extension method on your Window Loaded and Closing events to restore and save Window size and location.
using YourProject.Properties;
using System;
using System.Linq;
using System.Windows;
using System.Xml.Linq;
namespace YourProject.Extensions
{
public static class WindowExtensions
{
public static void SaveSizeAndLocation(this Window w)
{
try
{
var s = "<W>";
s += GetNode("Top", w.WindowState == WindowState.Maximized ? w.RestoreBounds.Top : w.Top);
s += GetNode("Left", w.WindowState == WindowState.Maximized ? w.RestoreBounds.Left : w.Left);
s += GetNode("Height", w.WindowState == WindowState.Maximized ? w.RestoreBounds.Height : w.Height);
s += GetNode("Width", w.WindowState == WindowState.Maximized ? w.RestoreBounds.Width : w.Width);
s += GetNode("WindowState", w.WindowState);
s += "</W>";
Settings.Default.WindowXml = s;
Settings.Default.Save();
}
catch (Exception)
{
}
}
public static void RestoreSizeAndLocation(this Window w)
{
try
{
var xd = XDocument.Parse(Settings.Default.WindowXml);
w.WindowState = (WindowState)Enum.Parse(typeof(WindowState), xd.Descendants("WindowState").FirstOrDefault().Value);
w.Top = Convert.ToDouble(xd.Descendants("Top").FirstOrDefault().Value);
w.Left = Convert.ToDouble(xd.Descendants("Left").FirstOrDefault().Value);
w.Height = Convert.ToDouble(xd.Descendants("Height").FirstOrDefault().Value);
w.Width = Convert.ToDouble(xd.Descendants("Width").FirstOrDefault().Value);
}
catch (Exception)
{
}
}
private static string GetNode(string name, object value)
{
return string.Format("<{0}>{1}</{0}>", name, value);
}
}
}