.NET WPF记住会话之间的窗口大小

基本上,当用户调整我的应用程序的窗口时,我希望应用程序再次重新打开时,应用程序是相同的大小。

起初我虽然处理SizeChanged事件并保存高度和宽度,但我认为必须有更容易的解决scheme。

很简单的问题,但我找不到简单的解决scheme。

将值保存在user.config文件中。

您需要在设置文件中创建值 – 它应该在属性文件夹中。 创建五个值:

  • 类型double Top
  • Left的类型double
  • 类型double Height
  • 类型的Width double
  • Maximized bool类型 – 保持窗口是否最大化。 如果你想存储更多的信息,那么将需要一个不同的类型或结构。

初始化前两个为0,后两个为应用程序的默认大小,最后一个为false。

在构造函数中:

 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; } 

创建一个Window_Closing事件处理程序并添加以下内容:

 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(); 

如果用户使显示区域更小(无论是通过断开屏幕或更改屏幕分辨率),则应用程序关闭时,这将失败,因此您应该在应用值之前添加检查,以确定所需位置和大小仍然有效。

其实你不需要使用代码隐藏(除了保存设置)。 您可以使用自定义标记扩展来将窗口大小和位置绑定到像这样的设置:

 <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}"> 

你可以在这里找到这个标记扩展的代码: http : //www.thomaslevesque.com/2008/11/18/wpf-binding-to-application-settings-using-a-markup-extension/

虽然你可以“滚动你自己的”,并手动保存设置的地方,一般来说,它会工作,很容易不正确处理所有的情况。 通过在出口调用GetWindowPlacement()和在启动时调用SetWindowPlacement() ,让操作系统为您完成工作要好得多。 它处理所有可能发生的疯狂边缘情况(多个监视器,如果最大化关闭时保存窗口的正常大小,等等),所以你不必这样做。

此MSDN示例演示如何将这些与WPF应用程序一起使用。 该示例并不完美(窗口将在第一次运行时尽可能小的左上角开始,并且设置设计器保存一个WINDOWPLACEMENT类型的值有一些奇怪的行为),但它应该至少让你开始。

刚刚写了一个博客条目,详细说明如何以一个简单而强大的方式做到这一点。 它使用了Andy提到的GetWindowPlacement和SetWindowPlacement函数,但是他提到的一些奇怪的行为已经被清除了:

http://blogs.msdn.com/davidrickard/archive/2010/03/09/saving-window-size-and-location-in-wpf-and-winforms.aspx

上面Thomas发布的“长格式”绑定几乎不需要编码,只要确保你有命名空间绑定:

 <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}"> 

然后保存在代码后面:

 private void frmMain_Closed(object sender, EventArgs e) { Properties.Settings.Default.Save(); } 

或者,你也可能喜欢以下的方法( 见源代码 )。 将WindowSettings类添加到您的项目中,并在主窗口的标题中插入WindowSettings.Save="True"

 <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"> 

WindowSettings的定义如下:

 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 } } 

解决它的默认方法是使用设置文件。 设置文件的问题是您必须定义所有的设置并编写自己来回复制数据的代码。 如果你有很多的财产要跟踪很乏味。

我为此做了一个非常灵活和非常容易使用的库,你只要告诉它跟踪哪个对象的属性,剩下的就完成了。 如果你喜欢,你也可以配置它。

这个库被称为Jot(github) ,这是我写的关于它的一个旧的CodeProject文章 。

以下是您如何使用它来跟踪窗口的大小和位置:

 public MainWindow() { InitializeComponent(); _stateTracker.Configure(this) .IdentifyAs("MyMainWindow") .AddProperties(nameof(Height), nameof(Width), nameof(Left), nameof(Top), nameof(WindowState)) .RegisterPersistTrigger(nameof(Closed)) .Apply(); } 

Jot与设置文件:使用Jot的代码相当少,而且由于您只需要提及每个属性一次 ,所以错误更少。 使用设置文件,您需要提及每个属性5次 :一次显式创建属性,另外四次在代码中来回复制值。

存储,序列化等是完全可配置的。 此外,使用IOC时,甚至可以将其挂钩,以便自动将跟踪应用到所解析的所有对象,因此,只需要使属性持久化,就可以掌握一个[Trackable]属性。

我正在写这一切,因为我认为图书馆是一流的,我想说一说。

这是我在互联网上看到的最优雅的解决方案。 看一下这个:

http://blogs.msdn.com/b/davidrickard/archive/2010/03/09/saving-window-size-and-location-in-wpf-and-winforms.aspx

它也适用于WPF和WinForms。

我写了一个快速的课程。 这是如何被称为:

  public MainWindow() { FormSizeSaver.RegisterForm(this, () => Settings.Default.MainWindowSettings, s => { Settings.Default.MainWindowSettings = s; Settings.Default.Save(); }); InitializeComponent(); ... 

这里是代码:

 public class FormSizeSaver { private readonly Window window; private readonly Func<FormSizeSaverSettings> getSetting; private readonly Action<FormSizeSaverSettings> saveSetting; private FormSizeSaver(Window window, Func<string> getSetting, Action<string> saveSetting) { this.window = window; this.getSetting = () => FormSizeSaverSettings.FromString(getSetting()); this.saveSetting = s => saveSetting(s.ToString()); window.Initialized += InitializedHandler; window.StateChanged += StateChangedHandler; window.SizeChanged += SizeChangedHandler; window.LocationChanged += LocationChangedHandler; } public static FormSizeSaver RegisterForm(Window window, Func<string> getSetting, Action<string> saveSetting) { return new FormSizeSaver(window, getSetting, saveSetting); } private void SizeChangedHandler(object sender, SizeChangedEventArgs e) { var s = getSetting(); s.Height = e.NewSize.Height; s.Width = e.NewSize.Width; saveSetting(s); } private void StateChangedHandler(object sender, EventArgs e) { var s = getSetting(); if (window.WindowState == WindowState.Maximized) { if (!s.Maximized) { s.Maximized = true; saveSetting(s); } } else if (window.WindowState == WindowState.Normal) { if (s.Maximized) { s.Maximized = false; saveSetting(s); } } } private void InitializedHandler(object sender, EventArgs e) { var s = getSetting(); window.WindowState = s.Maximized ? WindowState.Maximized : WindowState.Normal; if (s.Height != 0 && s.Width != 0) { window.Height = s.Height; window.Width = s.Width; window.WindowStartupLocation = WindowStartupLocation.Manual; window.Left = s.XLoc; window.Top = s.YLoc; } } private void LocationChangedHandler(object sender, EventArgs e) { var s = getSetting(); s.XLoc = window.Left; s.YLoc = window.Top; saveSetting(s); } } [Serializable] internal class FormSizeSaverSettings { public double Height, Width, YLoc, XLoc; public bool Maximized; public override string ToString() { using (var ms = new MemoryStream()) { var bf = new BinaryFormatter(); bf.Serialize(ms, this); ms.Position = 0; byte[] buffer = new byte[(int)ms.Length]; ms.Read(buffer, 0, buffer.Length); return Convert.ToBase64String(buffer); } } internal static FormSizeSaverSettings FromString(string value) { try { using (var ms = new MemoryStream(Convert.FromBase64String(value))) { var bf = new BinaryFormatter(); return (FormSizeSaverSettings) bf.Deserialize(ms); } } catch (Exception) { return new FormSizeSaverSettings(); } } } 

你可能会喜欢这个:

 public class WindowStateHelper { public static string ToXml(System.Windows.Window win) { XElement bounds = new XElement("Bounds"); if (win.WindowState == System.Windows.WindowState.Maximized) { bounds.Add(new XElement("Top", win.RestoreBounds.Top)); bounds.Add(new XElement("Left", win.RestoreBounds.Left)); bounds.Add(new XElement("Height", win.RestoreBounds.Height)); bounds.Add(new XElement("Width", win.RestoreBounds.Width)); } else { bounds.Add(new XElement("Top", win.Top)); bounds.Add(new XElement("Left", win.Left)); bounds.Add(new XElement("Height", win.Height)); bounds.Add(new XElement("Width", win.Width)); } XElement root = new XElement("WindowState", new XElement("State", win.WindowState.ToString()), new XElement("Visibility", win.Visibility.ToString()), bounds); return root.ToString(); } public static void FromXml(string xml, System.Windows.Window win) { try { XElement root = XElement.Parse(xml); string state = root.Descendants("State").FirstOrDefault().Value; win.WindowState = (System.Windows.WindowState)Enum.Parse(typeof(System.Windows.WindowState), state); state = root.Descendants("Visibility").FirstOrDefault().Value; win.Visibility = (System.Windows.Visibility)Enum.Parse(typeof(System.Windows.Visibility), state); XElement bounds = root.Descendants("Bounds").FirstOrDefault(); win.Top = Convert.ToDouble(bounds.Element("Top").Value); win.Left = Convert.ToDouble(bounds.Element("Left").Value); win.Height = Convert.ToDouble(bounds.Element("Height").Value); win.Width = Convert.ToDouble(bounds.Element("Width").Value); } catch (Exception x) { System.Console.WriteLine(x.ToString()); } } } 

当应用程序关闭时:

  Properties.Settings.Default.Win1Placement = WindowStateHelper.ToXml(win1); Properties.Settings.Default.Win2Placement = WindowStateHelper.ToXml(win2); ... 

当应用程序启动时:

  WindowStateHelper.FromXml(Properties.Settings.Default.Win1Placement, win1); WindowStateHelper.FromXml(Properties.Settings.Default.Win2Placement, win2); ... 

在默认设置中创建一个名为WindowXml的字符串。

在“窗口加载”和“关闭”事件中使用此扩展方法来还原并保存窗口大小和位置。

 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); } } } 

有一个NuGet项目RestoreWindowPlace在github上看到,为你做这一切,将信息保存在一个XML文件。

为了让它在窗口上工作,就像调用一样简单:

((App)Application.Current).WindowPlace.Register(this, "MainWindow");

在应用程序中,您创建管理您的窗口的类。 请参阅上面的github链接了解更多信息。